Add base form action without logic (#10811)
<img width="1298" alt="Capture d’écran 2025-03-12 à 15 32 27" src="https://github.com/user-attachments/assets/8a3140e5-e165-445e-a718-748aa76b525c" />
This commit is contained in:
@ -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 = {
|
||||
|
||||
@ -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<Scalars['JSON']>;
|
||||
/** 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<typeof useGetTimelineThreadsFromPersonIdQuery>;
|
||||
export type GetTimelineThreadsFromPersonIdLazyQueryHookResult = ReturnType<typeof useGetTimelineThreadsFromPersonIdLazyQuery>;
|
||||
export type GetTimelineThreadsFromPersonIdQueryResult = Apollo.QueryResult<GetTimelineThreadsFromPersonIdQuery, GetTimelineThreadsFromPersonIdQueryVariables>;
|
||||
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<EmptyQuery, EmptyQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<EmptyQuery, EmptyQueryVariables>(EmptyDocument, options);
|
||||
}
|
||||
export function useEmptyLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EmptyQuery, EmptyQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<EmptyQuery, EmptyQueryVariables>(EmptyDocument, options);
|
||||
}
|
||||
export type EmptyQueryHookResult = ReturnType<typeof useEmptyQuery>;
|
||||
export type EmptyLazyQueryHookResult = ReturnType<typeof useEmptyLazyQuery>;
|
||||
export type EmptyQueryResult = Apollo.QueryResult<EmptyQuery, EmptyQueryVariables>;
|
||||
export const TrackDocument = gql`
|
||||
mutation Track($action: String!, $payload: JSON!) {
|
||||
track(action: $action, payload: $payload) {
|
||||
|
||||
@ -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 (
|
||||
<RecordFilterGroupsComponentInstanceContext.Provider
|
||||
@ -92,7 +77,7 @@ const meta: Meta<typeof CommandMenu> = {
|
||||
commandMenuNavigationStackState,
|
||||
);
|
||||
|
||||
setCurrentWorkspace(mockWorkspaceWithFeatureFlag);
|
||||
setCurrentWorkspace(mockCurrentWorkspace);
|
||||
setCurrentWorkspaceMember(mockedWorkspaceMemberData);
|
||||
setIsCommandMenuOpened(true);
|
||||
setCommandMenuNavigationStack([
|
||||
|
||||
@ -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 (
|
||||
<RightDrawerStepListContainer>
|
||||
@ -32,7 +37,9 @@ export const CommandMenuWorkflowSelectActionContent = ({
|
||||
<RightDrawerWorkflowSelectStepTitle>
|
||||
Other
|
||||
</RightDrawerWorkflowSelectStepTitle>
|
||||
{OTHER_ACTIONS.map((action) => (
|
||||
{OTHER_ACTIONS.filter(
|
||||
(action) => isWorkflowFormActionEnabled || action.type !== 'FORM',
|
||||
).map((action) => (
|
||||
<MenuItemCommand
|
||||
key={action.type}
|
||||
LeftIcon={getIcon(action.icon)}
|
||||
|
||||
@ -10,6 +10,8 @@ import {
|
||||
workflowDeleteRecordActionSettingsSchema,
|
||||
workflowFindRecordsActionSchema,
|
||||
workflowFindRecordsActionSettingsSchema,
|
||||
workflowFormActionSchema,
|
||||
workflowFormActionSettingsSchema,
|
||||
workflowManualTriggerSchema,
|
||||
workflowRunContextSchema,
|
||||
workflowRunOutputSchema,
|
||||
@ -42,6 +44,9 @@ export type WorkflowDeleteRecordActionSettings = z.infer<
|
||||
export type WorkflowFindRecordsActionSettings = z.infer<
|
||||
typeof workflowFindRecordsActionSettingsSchema
|
||||
>;
|
||||
export type WorkflowFormActionSettings = z.infer<
|
||||
typeof workflowFormActionSettingsSchema
|
||||
>;
|
||||
|
||||
export type WorkflowCodeAction = z.infer<typeof workflowCodeActionSchema>;
|
||||
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<typeof workflowFormActionSchema>;
|
||||
|
||||
export type WorkflowAction = z.infer<typeof workflowActionSchema>;
|
||||
export type WorkflowActionType = WorkflowAction['type'];
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 (
|
||||
<Suspense fallback={<RightDrawerSkeletonLoader />}>
|
||||
{props.readonly ? (
|
||||
<WorkflowReadonlyActionFormServerlessFunction
|
||||
<WorkflowReadonlyActionServerlessFunction
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
/>
|
||||
) : (
|
||||
<WorkflowEditActionFormServerlessFunction
|
||||
<WorkflowEditActionServerlessFunction
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
@ -122,7 +123,7 @@ export const WorkflowStepDetail = ({
|
||||
}
|
||||
case 'SEND_EMAIL': {
|
||||
return (
|
||||
<WorkflowEditActionFormSendEmail
|
||||
<WorkflowEditActionSendEmail
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
@ -131,7 +132,7 @@ export const WorkflowStepDetail = ({
|
||||
}
|
||||
case 'CREATE_RECORD': {
|
||||
return (
|
||||
<WorkflowEditActionFormCreateRecord
|
||||
<WorkflowEditActionCreateRecord
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
@ -141,7 +142,7 @@ export const WorkflowStepDetail = ({
|
||||
|
||||
case 'UPDATE_RECORD': {
|
||||
return (
|
||||
<WorkflowEditActionFormUpdateRecord
|
||||
<WorkflowEditActionUpdateRecord
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
@ -151,7 +152,7 @@ export const WorkflowStepDetail = ({
|
||||
|
||||
case 'DELETE_RECORD': {
|
||||
return (
|
||||
<WorkflowEditActionFormDeleteRecord
|
||||
<WorkflowEditActionDeleteRecord
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
@ -161,7 +162,17 @@ export const WorkflowStepDetail = ({
|
||||
|
||||
case 'FIND_RECORDS': {
|
||||
return (
|
||||
<WorkflowEditActionFormFindRecords
|
||||
<WorkflowEditActionFindRecords
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
case 'FORM': {
|
||||
return (
|
||||
<WorkflowEditActionForm
|
||||
key={stepId}
|
||||
action={stepDefinition.definition}
|
||||
actionOptions={props}
|
||||
|
||||
@ -17,7 +17,7 @@ import { JsonValue } from 'type-fest';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
|
||||
type WorkflowEditActionFormCreateRecordProps = {
|
||||
type WorkflowEditActionCreateRecordProps = {
|
||||
action: WorkflowCreateRecordAction;
|
||||
actionOptions:
|
||||
| {
|
||||
@ -53,10 +53,10 @@ const sortByViewFieldPosition = (
|
||||
return 0;
|
||||
};
|
||||
|
||||
export const WorkflowEditActionFormCreateRecord = ({
|
||||
export const WorkflowEditActionCreateRecord = ({
|
||||
action,
|
||||
actionOptions,
|
||||
}: WorkflowEditActionFormCreateRecordProps) => {
|
||||
}: WorkflowEditActionCreateRecordProps) => {
|
||||
const theme = useTheme();
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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 (
|
||||
<>
|
||||
<WorkflowStepHeader
|
||||
onTitleChange={(newName: string) => {
|
||||
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}
|
||||
/>
|
||||
<WorkflowStepBody>
|
||||
{action.settings.input.map((field) => (
|
||||
<FormFieldInputContainer key={field.name}>
|
||||
{field.label ? <InputLabel>{field.label}</InputLabel> : null}
|
||||
|
||||
<FormFieldInputRowContainer>
|
||||
<FormFieldInputInputContainer hasRightElement={false}>
|
||||
<StyledContainer onClick={() => {}}>
|
||||
<StyledPlaceholder>{field.placeholder}</StyledPlaceholder>
|
||||
<IconChevronDown
|
||||
size={theme.icon.size.md}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
</StyledContainer>
|
||||
</FormFieldInputInputContainer>
|
||||
</FormFieldInputRowContainer>
|
||||
</FormFieldInputContainer>
|
||||
))}
|
||||
{!actionOptions.readonly && (
|
||||
<FormFieldInputContainer>
|
||||
<FormFieldInputRowContainer>
|
||||
<FormFieldInputInputContainer hasRightElement={false}>
|
||||
<StyledContainer onClick={() => {}}>
|
||||
<StyledAddFieldContainer>
|
||||
<IconPlus size={theme.icon.size.sm} />
|
||||
{t`Add Field`}
|
||||
</StyledAddFieldContainer>
|
||||
</StyledContainer>
|
||||
</FormFieldInputInputContainer>
|
||||
</FormFieldInputRowContainer>
|
||||
</FormFieldInputContainer>
|
||||
)}
|
||||
</WorkflowStepBody>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -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);
|
||||
@ -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 = ({
|
||||
<WorkflowStepBody>
|
||||
{activeTabId === 'code' && (
|
||||
<>
|
||||
<WorkflowEditActionFormServerlessFunctionFields
|
||||
<WorkflowEditActionServerlessFunctionFields
|
||||
functionInput={functionInput}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
onInputChange={handleInputChange}
|
||||
@ -327,7 +327,7 @@ export const WorkflowEditActionFormServerlessFunction = ({
|
||||
)}
|
||||
{activeTabId === 'test' && (
|
||||
<>
|
||||
<WorkflowEditActionFormServerlessFunctionFields
|
||||
<WorkflowEditActionServerlessFunctionFields
|
||||
functionInput={serverlessFunctionTestData.input}
|
||||
onInputChange={handleTestInputChange}
|
||||
readonly={actionOptions.readonly}
|
||||
@ -11,7 +11,7 @@ const StyledContainer = styled.div`
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
type WorkflowEditActionFormServerlessFunctionFieldsProps = {
|
||||
type WorkflowEditActionServerlessFunctionFieldsProps = {
|
||||
functionInput: FunctionInput;
|
||||
path?: string[];
|
||||
readonly?: boolean;
|
||||
@ -19,13 +19,13 @@ type WorkflowEditActionFormServerlessFunctionFieldsProps = {
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
};
|
||||
|
||||
export const WorkflowEditActionFormServerlessFunctionFields = ({
|
||||
export const WorkflowEditActionServerlessFunctionFields = ({
|
||||
functionInput,
|
||||
path = [],
|
||||
readonly,
|
||||
onInputChange,
|
||||
VariablePicker,
|
||||
}: WorkflowEditActionFormServerlessFunctionFieldsProps) => {
|
||||
}: WorkflowEditActionServerlessFunctionFieldsProps) => {
|
||||
return (
|
||||
<>
|
||||
{Object.entries(functionInput).map(([inputKey, inputValue]) => {
|
||||
@ -37,7 +37,7 @@ export const WorkflowEditActionFormServerlessFunctionFields = ({
|
||||
<StyledContainer key={pathKey}>
|
||||
<InputLabel>{inputKey}</InputLabel>
|
||||
<FormNestedFieldInputContainer>
|
||||
<WorkflowEditActionFormServerlessFunctionFields
|
||||
<WorkflowEditActionServerlessFunctionFields
|
||||
functionInput={inputValue}
|
||||
path={currentPath}
|
||||
readonly={readonly}
|
||||
@ -18,7 +18,7 @@ import { JsonValue } from 'type-fest';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||
|
||||
type WorkflowEditActionFormUpdateRecordProps = {
|
||||
type WorkflowEditActionUpdateRecordProps = {
|
||||
action: WorkflowUpdateRecordAction;
|
||||
actionOptions:
|
||||
| {
|
||||
@ -55,10 +55,10 @@ const AVAILABLE_FIELD_METADATA_TYPES = [
|
||||
FieldMetadataType.UUID,
|
||||
];
|
||||
|
||||
export const WorkflowEditActionFormUpdateRecord = ({
|
||||
export const WorkflowEditActionUpdateRecord = ({
|
||||
action,
|
||||
actionOptions,
|
||||
}: WorkflowEditActionFormUpdateRecordProps) => {
|
||||
}: WorkflowEditActionUpdateRecordProps) => {
|
||||
const theme = useTheme();
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
@ -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
|
||||
/>
|
||||
<WorkflowStepBody>
|
||||
<WorkflowEditActionFormServerlessFunctionFields
|
||||
<WorkflowEditActionServerlessFunctionFields
|
||||
functionInput={action.settings.input.serverlessFunctionInput}
|
||||
readonly
|
||||
/>
|
||||
@ -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<typeof WorkflowEditActionFormCreateRecord> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionFormCreateRecord',
|
||||
component: WorkflowEditActionFormCreateRecord,
|
||||
const meta: Meta<typeof WorkflowEditActionCreateRecord> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionCreateRecord',
|
||||
component: WorkflowEditActionCreateRecord,
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
@ -54,7 +54,7 @@ const meta: Meta<typeof WorkflowEditActionFormCreateRecord> = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof WorkflowEditActionFormCreateRecord>;
|
||||
type Story = StoryObj<typeof WorkflowEditActionCreateRecord>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
@ -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<typeof WorkflowEditActionFormDeleteRecord> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionFormDeleteRecord',
|
||||
component: WorkflowEditActionFormDeleteRecord,
|
||||
const meta: Meta<typeof WorkflowEditActionDeleteRecord> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionDeleteRecord',
|
||||
component: WorkflowEditActionDeleteRecord,
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
@ -58,7 +58,7 @@ const meta: Meta<typeof WorkflowEditActionFormDeleteRecord> = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof WorkflowEditActionFormDeleteRecord>;
|
||||
type Story = StoryObj<typeof WorkflowEditActionDeleteRecord>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
@ -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<typeof WorkflowEditActionFormFindRecords> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionFormFindRecords',
|
||||
component: WorkflowEditActionFormFindRecords,
|
||||
const meta: Meta<typeof WorkflowEditActionFindRecords> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionFindRecords',
|
||||
component: WorkflowEditActionFindRecords,
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
@ -55,7 +55,7 @@ const meta: Meta<typeof WorkflowEditActionFormFindRecords> = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof WorkflowEditActionFormFindRecords>;
|
||||
type Story = StoryObj<typeof WorkflowEditActionFindRecords>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
@ -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<typeof WorkflowEditActionForm> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionForm',
|
||||
component: WorkflowEditActionForm,
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
args: {
|
||||
action: DEFAULT_ACTION,
|
||||
},
|
||||
decorators: [
|
||||
WorkflowStepActionDrawerDecorator,
|
||||
ComponentDecorator,
|
||||
RouterDecorator,
|
||||
I18nFrontDecorator,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof WorkflowEditActionForm>;
|
||||
|
||||
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();
|
||||
},
|
||||
};
|
||||
@ -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<typeof WorkflowEditActionFormUpdateRecord> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionFormUpdateRecord',
|
||||
component: WorkflowEditActionFormUpdateRecord,
|
||||
const meta: Meta<typeof WorkflowEditActionUpdateRecord> = {
|
||||
title: 'Modules/Workflow/WorkflowEditActionUpdateRecord',
|
||||
component: WorkflowEditActionUpdateRecord,
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
},
|
||||
@ -71,7 +71,7 @@ const meta: Meta<typeof WorkflowEditActionFormUpdateRecord> = {
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof WorkflowEditActionFormUpdateRecord>;
|
||||
type Story = StoryObj<typeof WorkflowEditActionUpdateRecord>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
@ -15,4 +15,9 @@ export const OTHER_ACTIONS: Array<{
|
||||
type: 'CODE',
|
||||
icon: 'IconCode',
|
||||
},
|
||||
{
|
||||
label: 'Form',
|
||||
type: 'FORM',
|
||||
icon: 'IconForms',
|
||||
},
|
||||
];
|
||||
|
||||
@ -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',
|
||||
// },
|
||||
];
|
||||
|
||||
@ -70,6 +70,11 @@ export const seedFeatureFlags = async (
|
||||
workspaceId: workspaceId,
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
key: FeatureFlagKey.IsWorkflowFormActionEnabled,
|
||||
workspaceId: workspaceId,
|
||||
value: true,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
};
|
||||
|
||||
@ -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',
|
||||
}
|
||||
|
||||
@ -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`,
|
||||
|
||||
Reference in New Issue
Block a user