diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx index c817b8d764..a2bcf266c8 100644 --- a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx +++ b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx @@ -5,13 +5,14 @@ import { ApolloError } from '@apollo/client'; import { verifyEmailRedirectPathState } from '@/app/states/verifyEmailRedirectPathState'; import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin'; +import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState'; import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace'; import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; import { Modal } from '@/ui/layout/modal/components/Modal'; import { useLingui } from '@lingui/react/macro'; import { useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { useSetRecoilState } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { useNavigateApp } from '~/hooks/useNavigateApp'; import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl'; @@ -40,6 +41,7 @@ export const VerifyEmailEffect = () => { const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain(); const { verifyLoginToken } = useVerifyLogin(); const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace(); + const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState); const { t } = useLingui(); useEffect(() => { @@ -112,10 +114,15 @@ export const VerifyEmailEffect = () => { } }; + if (!clientConfigApiStatus.isLoadedOnce) { + return; + } + verifyEmailToken(); + // Verify email only needs to run once at mount // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [clientConfigApiStatus.isLoadedOnce]); if (isError) { return ( diff --git a/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx index 977bd22e39..a596d27c26 100644 --- a/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx +++ b/packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx @@ -1,105 +1,14 @@ import { useClientConfig } from '@/client-config/hooks/useClientConfig'; -import { aiModelsState } from '@/client-config/states/aiModelsState'; -import { apiConfigState } from '@/client-config/states/apiConfigState'; -import { appVersionState } from '@/client-config/states/appVersionState'; -import { authProvidersState } from '@/client-config/states/authProvidersState'; -import { billingState } from '@/client-config/states/billingState'; -import { calendarBookingPageIdState } from '@/client-config/states/calendarBookingPageIdState'; -import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState'; -import { captchaState } from '@/client-config/states/captchaState'; -import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState'; import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState'; -import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabledState'; -import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState'; -import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState'; -import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState'; -import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState'; -import { isGoogleCalendarEnabledState } from '@/client-config/states/isGoogleCalendarEnabledState'; -import { isGoogleMessagingEnabledState } from '@/client-config/states/isGoogleMessagingEnabledState'; -import { isImapSmtpCaldavEnabledState } from '@/client-config/states/isImapSmtpCaldavEnabledState'; -import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicrosoftCalendarEnabledState'; -import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState'; -import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; -import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState'; -import { sentryConfigState } from '@/client-config/states/sentryConfigState'; -import { supportChatState } from '@/client-config/states/supportChatState'; -import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; import { useEffect } from 'react'; -import { useRecoilState, useSetRecoilState } from 'recoil'; +import { useRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const ClientConfigProviderEffect = () => { - const setIsAnalyticsEnabled = useSetRecoilState(isAnalyticsEnabledState); - const setDomainConfiguration = useSetRecoilState(domainConfigurationState); - const setAuthProviders = useSetRecoilState(authProvidersState); - const setAiModels = useSetRecoilState(aiModelsState); - - const setIsDeveloperDefaultSignInPrefilled = useSetRecoilState( - isDeveloperDefaultSignInPrefilledState, - ); - const setIsMultiWorkspaceEnabled = useSetRecoilState( - isMultiWorkspaceEnabledState, - ); - const setIsEmailVerificationRequired = useSetRecoilState( - isEmailVerificationRequiredState, - ); - - const setBilling = useSetRecoilState(billingState); - const setSupportChat = useSetRecoilState(supportChatState); - - const setSentryConfig = useSetRecoilState(sentryConfigState); const [clientConfigApiStatus, setClientConfigApiStatus] = useRecoilState( clientConfigApiStatusState, ); - const setCaptcha = useSetRecoilState(captchaState); - - const setChromeExtensionId = useSetRecoilState(chromeExtensionIdState); - - const setApiConfig = useSetRecoilState(apiConfigState); - - const setCanManageFeatureFlags = useSetRecoilState( - canManageFeatureFlagsState, - ); - - const setLabPublicFeatureFlags = useSetRecoilState( - labPublicFeatureFlagsState, - ); - - const setMicrosoftMessagingEnabled = useSetRecoilState( - isMicrosoftMessagingEnabledState, - ); - - const setMicrosoftCalendarEnabled = useSetRecoilState( - isMicrosoftCalendarEnabledState, - ); - - const setGoogleMessagingEnabled = useSetRecoilState( - isGoogleMessagingEnabledState, - ); - - const setGoogleCalendarEnabled = useSetRecoilState( - isGoogleCalendarEnabledState, - ); - - const setIsAttachmentPreviewEnabled = useSetRecoilState( - isAttachmentPreviewEnabledState, - ); - - const setIsConfigVariablesInDbEnabled = useSetRecoilState( - isConfigVariablesInDbEnabledState, - ); - - const setCalendarBookingPageId = useSetRecoilState( - calendarBookingPageIdState, - ); - - const setIsImapSmtpCaldavEnabled = useSetRecoilState( - isImapSmtpCaldavEnabledState, - ); - - const setAppVersion = useSetRecoilState(appVersionState); - const { data, loading, error, fetchClientConfig } = useClientConfig(); useEffect(() => { @@ -130,98 +39,7 @@ export const ClientConfigProviderEffect = () => { if (!isDefined(data?.clientConfig)) { return; } - - setClientConfigApiStatus((currentStatus) => ({ - ...currentStatus, - isErrored: false, - error: undefined, - })); - setAppVersion(data.clientConfig.appVersion); - setAuthProviders({ - google: data?.clientConfig.authProviders.google, - microsoft: data?.clientConfig.authProviders.microsoft, - password: data?.clientConfig.authProviders.password, - magicLink: false, - sso: data?.clientConfig.authProviders.sso, - }); - setAiModels(data?.clientConfig.aiModels || []); - setIsAnalyticsEnabled(data?.clientConfig.analyticsEnabled); - setIsDeveloperDefaultSignInPrefilled(data?.clientConfig.signInPrefilled); - setIsMultiWorkspaceEnabled(data?.clientConfig.isMultiWorkspaceEnabled); - setIsEmailVerificationRequired( - data?.clientConfig.isEmailVerificationRequired, - ); - setBilling(data?.clientConfig.billing); - setSupportChat(data?.clientConfig.support); - - setSentryConfig({ - dsn: data?.clientConfig?.sentry?.dsn, - release: data?.clientConfig?.sentry?.release, - environment: data?.clientConfig?.sentry?.environment, - }); - - setCaptcha({ - provider: data?.clientConfig?.captcha?.provider, - siteKey: data?.clientConfig?.captcha?.siteKey, - }); - - setChromeExtensionId(data?.clientConfig?.chromeExtensionId); - setApiConfig(data?.clientConfig?.api); - setDomainConfiguration({ - defaultSubdomain: data?.clientConfig?.defaultSubdomain, - frontDomain: data?.clientConfig?.frontDomain, - }); - setCanManageFeatureFlags(data?.clientConfig?.canManageFeatureFlags); - setLabPublicFeatureFlags(data?.clientConfig?.publicFeatureFlags); - setMicrosoftMessagingEnabled( - data?.clientConfig?.isMicrosoftMessagingEnabled, - ); - setMicrosoftCalendarEnabled(data?.clientConfig?.isMicrosoftCalendarEnabled); - setGoogleMessagingEnabled(data?.clientConfig?.isGoogleMessagingEnabled); - setGoogleCalendarEnabled(data?.clientConfig?.isGoogleCalendarEnabled); - setIsAttachmentPreviewEnabled( - data?.clientConfig?.isAttachmentPreviewEnabled, - ); - setIsConfigVariablesInDbEnabled( - data?.clientConfig?.isConfigVariablesInDbEnabled, - ); - setClientConfigApiStatus((currentStatus) => ({ - ...currentStatus, - isSaved: true, - })); - - setCalendarBookingPageId(data?.clientConfig?.calendarBookingPageId ?? null); - setIsImapSmtpCaldavEnabled(data?.clientConfig?.isImapSmtpCaldavEnabled); - }, [ - data, - loading, - error, - setIsDeveloperDefaultSignInPrefilled, - setIsMultiWorkspaceEnabled, - setIsEmailVerificationRequired, - setSupportChat, - setBilling, - setSentryConfig, - setClientConfigApiStatus, - setCaptcha, - setChromeExtensionId, - setApiConfig, - setIsAnalyticsEnabled, - setDomainConfiguration, - setAuthProviders, - setAiModels, - setCanManageFeatureFlags, - setLabPublicFeatureFlags, - setMicrosoftMessagingEnabled, - setMicrosoftCalendarEnabled, - setGoogleMessagingEnabled, - setGoogleCalendarEnabled, - setIsAttachmentPreviewEnabled, - setIsConfigVariablesInDbEnabled, - setCalendarBookingPageId, - setIsImapSmtpCaldavEnabled, - setAppVersion, - ]); + }, [data?.clientConfig, error, loading, setClientConfigApiStatus]); return <>; }; diff --git a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts index c92a961818..262da88689 100644 --- a/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts +++ b/packages/twenty-front/src/modules/client-config/hooks/useClientConfig.ts @@ -1,6 +1,30 @@ +import { aiModelsState } from '@/client-config/states/aiModelsState'; +import { apiConfigState } from '@/client-config/states/apiConfigState'; +import { appVersionState } from '@/client-config/states/appVersionState'; +import { authProvidersState } from '@/client-config/states/authProvidersState'; +import { billingState } from '@/client-config/states/billingState'; +import { calendarBookingPageIdState } from '@/client-config/states/calendarBookingPageIdState'; +import { canManageFeatureFlagsState } from '@/client-config/states/canManageFeatureFlagsState'; +import { captchaState } from '@/client-config/states/captchaState'; +import { chromeExtensionIdState } from '@/client-config/states/chromeExtensionIdState'; +import { isAnalyticsEnabledState } from '@/client-config/states/isAnalyticsEnabledState'; +import { isAttachmentPreviewEnabledState } from '@/client-config/states/isAttachmentPreviewEnabledState'; +import { isConfigVariablesInDbEnabledState } from '@/client-config/states/isConfigVariablesInDbEnabledState'; +import { isDeveloperDefaultSignInPrefilledState } from '@/client-config/states/isDeveloperDefaultSignInPrefilledState'; +import { isEmailVerificationRequiredState } from '@/client-config/states/isEmailVerificationRequiredState'; +import { isGoogleCalendarEnabledState } from '@/client-config/states/isGoogleCalendarEnabledState'; +import { isGoogleMessagingEnabledState } from '@/client-config/states/isGoogleMessagingEnabledState'; +import { isImapSmtpCaldavEnabledState } from '@/client-config/states/isImapSmtpCaldavEnabledState'; +import { isMicrosoftCalendarEnabledState } from '@/client-config/states/isMicrosoftCalendarEnabledState'; +import { isMicrosoftMessagingEnabledState } from '@/client-config/states/isMicrosoftMessagingEnabledState'; +import { isMultiWorkspaceEnabledState } from '@/client-config/states/isMultiWorkspaceEnabledState'; +import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState'; +import { sentryConfigState } from '@/client-config/states/sentryConfigState'; +import { supportChatState } from '@/client-config/states/supportChatState'; import { type ClientConfig } from '@/client-config/types/ClientConfig'; +import { domainConfigurationState } from '@/domain-manager/states/domainConfigurationState'; import { useCallback } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilState, useSetRecoilState } from 'recoil'; import { clientConfigApiStatusState } from '../states/clientConfigApiStatusState'; import { getClientConfig } from '../utils/getClientConfig'; @@ -13,11 +37,77 @@ type UseClientConfigResult = { }; export const useClientConfig = (): UseClientConfigResult => { - const clientConfigApiStatus = useRecoilValue(clientConfigApiStatusState); - const setClientConfigApiStatus = useSetRecoilState( + const setIsAnalyticsEnabled = useSetRecoilState(isAnalyticsEnabledState); + const setDomainConfiguration = useSetRecoilState(domainConfigurationState); + const setAuthProviders = useSetRecoilState(authProvidersState); + const setAiModels = useSetRecoilState(aiModelsState); + + const setIsDeveloperDefaultSignInPrefilled = useSetRecoilState( + isDeveloperDefaultSignInPrefilledState, + ); + const setIsMultiWorkspaceEnabled = useSetRecoilState( + isMultiWorkspaceEnabledState, + ); + const setIsEmailVerificationRequired = useSetRecoilState( + isEmailVerificationRequiredState, + ); + + const setBilling = useSetRecoilState(billingState); + const setSupportChat = useSetRecoilState(supportChatState); + + const setSentryConfig = useSetRecoilState(sentryConfigState); + const [clientConfigApiStatus, setClientConfigApiStatus] = useRecoilState( clientConfigApiStatusState, ); + const setCaptcha = useSetRecoilState(captchaState); + + const setChromeExtensionId = useSetRecoilState(chromeExtensionIdState); + + const setApiConfig = useSetRecoilState(apiConfigState); + + const setCanManageFeatureFlags = useSetRecoilState( + canManageFeatureFlagsState, + ); + + const setLabPublicFeatureFlags = useSetRecoilState( + labPublicFeatureFlagsState, + ); + + const setMicrosoftMessagingEnabled = useSetRecoilState( + isMicrosoftMessagingEnabledState, + ); + + const setMicrosoftCalendarEnabled = useSetRecoilState( + isMicrosoftCalendarEnabledState, + ); + + const setGoogleMessagingEnabled = useSetRecoilState( + isGoogleMessagingEnabledState, + ); + + const setGoogleCalendarEnabled = useSetRecoilState( + isGoogleCalendarEnabledState, + ); + + const setIsAttachmentPreviewEnabled = useSetRecoilState( + isAttachmentPreviewEnabledState, + ); + + const setIsConfigVariablesInDbEnabled = useSetRecoilState( + isConfigVariablesInDbEnabledState, + ); + + const setCalendarBookingPageId = useSetRecoilState( + calendarBookingPageIdState, + ); + + const setIsImapSmtpCaldavEnabled = useSetRecoilState( + isImapSmtpCaldavEnabledState, + ); + + const setAppVersion = useSetRecoilState(appVersionState); + const fetchClientConfig = useCallback(async () => { setClientConfigApiStatus((prev) => ({ ...prev, @@ -34,6 +124,61 @@ export const useClientConfig = (): UseClientConfigResult => { error: undefined, data: { clientConfig }, })); + setClientConfigApiStatus((currentStatus) => ({ + ...currentStatus, + isErrored: false, + error: undefined, + })); + setAppVersion(clientConfig.appVersion); + setAuthProviders({ + google: clientConfig.authProviders.google, + microsoft: clientConfig.authProviders.microsoft, + password: clientConfig.authProviders.password, + magicLink: false, + sso: clientConfig.authProviders.sso, + }); + setAiModels(clientConfig.aiModels || []); + setIsAnalyticsEnabled(clientConfig.analyticsEnabled); + setIsDeveloperDefaultSignInPrefilled(clientConfig.signInPrefilled); + setIsMultiWorkspaceEnabled(clientConfig.isMultiWorkspaceEnabled); + setIsEmailVerificationRequired(clientConfig.isEmailVerificationRequired); + setBilling(clientConfig.billing); + setSupportChat(clientConfig.support); + + setSentryConfig({ + dsn: clientConfig?.sentry?.dsn, + release: clientConfig?.sentry?.release, + environment: clientConfig?.sentry?.environment, + }); + + setCaptcha({ + provider: clientConfig?.captcha?.provider, + siteKey: clientConfig?.captcha?.siteKey, + }); + + setChromeExtensionId(clientConfig?.chromeExtensionId); + setApiConfig(clientConfig?.api); + setDomainConfiguration({ + defaultSubdomain: clientConfig?.defaultSubdomain, + frontDomain: clientConfig?.frontDomain, + }); + setCanManageFeatureFlags(clientConfig?.canManageFeatureFlags); + setLabPublicFeatureFlags(clientConfig?.publicFeatureFlags); + setMicrosoftMessagingEnabled(clientConfig?.isMicrosoftMessagingEnabled); + setMicrosoftCalendarEnabled(clientConfig?.isMicrosoftCalendarEnabled); + setGoogleMessagingEnabled(clientConfig?.isGoogleMessagingEnabled); + setGoogleCalendarEnabled(clientConfig?.isGoogleCalendarEnabled); + setIsAttachmentPreviewEnabled(clientConfig?.isAttachmentPreviewEnabled); + setIsConfigVariablesInDbEnabled( + clientConfig?.isConfigVariablesInDbEnabled, + ); + setClientConfigApiStatus((currentStatus) => ({ + ...currentStatus, + isSaved: true, + })); + + setCalendarBookingPageId(clientConfig?.calendarBookingPageId ?? null); + setIsImapSmtpCaldavEnabled(clientConfig?.isImapSmtpCaldavEnabled); } catch (err) { const error = err instanceof Error ? err : new Error('Failed to fetch client config'); @@ -45,7 +190,33 @@ export const useClientConfig = (): UseClientConfigResult => { error, })); } - }, [setClientConfigApiStatus]); + }, [ + setAiModels, + setApiConfig, + setAppVersion, + setAuthProviders, + setBilling, + setCalendarBookingPageId, + setCanManageFeatureFlags, + setCaptcha, + setChromeExtensionId, + setClientConfigApiStatus, + setDomainConfiguration, + setGoogleCalendarEnabled, + setGoogleMessagingEnabled, + setIsAnalyticsEnabled, + setIsAttachmentPreviewEnabled, + setIsConfigVariablesInDbEnabled, + setIsDeveloperDefaultSignInPrefilled, + setIsEmailVerificationRequired, + setIsImapSmtpCaldavEnabled, + setIsMultiWorkspaceEnabled, + setLabPublicFeatureFlags, + setMicrosoftCalendarEnabled, + setMicrosoftMessagingEnabled, + setSentryConfig, + setSupportChat, + ]); return { data: clientConfigApiStatus.data, diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx index 1106a0c565..9e300880e9 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterFieldSelect.tsx @@ -2,7 +2,7 @@ import { SelectControl } from '@/ui/input/components/SelectControl'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { useWorkflowVersionIdOrThrow } from '@/workflow/hooks/useWorkflowVersionIdOrThrow'; import { stepsOutputSchemaFamilySelector } from '@/workflow/states/selectors/stepsOutputSchemaFamilySelector'; -import { useGetFilterFieldMetadataItem } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useGetFilterFieldMetadataItem'; +import { useFilterFieldMetadataItem } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterFieldMetadataItem'; import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings'; import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext'; import { getViewFilterOperands } from '@/workflow/workflow-steps/workflow-actions/filter-action/utils/getStepFilterOperands'; @@ -15,6 +15,7 @@ import { useContext } from 'react'; import { useRecoilCallback, useRecoilValue } from 'recoil'; import { type StepFilter } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; import { FieldMetadataType } from '~/generated-metadata/graphql'; type WorkflowStepFilterFieldSelectProps = { @@ -50,7 +51,12 @@ export const WorkflowStepFilterFieldSelect = ({ }), ); - const { getFilterFieldMetadataItem } = useGetFilterFieldMetadataItem(); + const { getIcon } = useIcons(); + + const { + fieldMetadataItem: filterFieldMetadataItem, + objectMetadataItem: filterObjectMetadataItem, + } = useFilterFieldMetadataItem(stepFilter.fieldMetadataId ?? ''); const availableVariablesInWorkflowStep = useAvailableVariablesInWorkflowStep({ shouldDisplayRecordFields, @@ -66,7 +72,7 @@ export const WorkflowStepFilterFieldSelect = ({ rawVariableName: variableName, part: 'stepId', }); - const currentStepOutputSchema = snapshot + const [currentStepOutputSchema] = snapshot .getLoadable( stepsOutputSchemaFamilySelector({ workflowVersionId, @@ -81,35 +87,25 @@ export const WorkflowStepFilterFieldSelect = ({ fieldMetadataId, compositeFieldSubFieldName, } = searchVariableThroughOutputSchema({ - stepOutputSchema: currentStepOutputSchema?.[0], + stepOutputSchema: currentStepOutputSchema, rawVariableName: variableName, isFullRecord: false, }); - const { - fieldMetadataItem: selectedFieldMetadataItem, - objectMetadataItem, - } = isDefined(fieldMetadataId) - ? getFilterFieldMetadataItem(fieldMetadataId) - : { - fieldMetadataItem: undefined, - objectMetadataItem: undefined, - }; - const filterType = isDefined(fieldMetadataId) - ? (selectedFieldMetadataItem?.type ?? 'unknown') + ? (filterFieldMetadataItem?.type ?? 'unknown') : variableType; const isFullRecord = - selectedFieldMetadataItem?.name === 'id' && - isDefined(objectMetadataItem?.labelSingular); + filterFieldMetadataItem?.name === 'id' && + isDefined(filterObjectMetadataItem?.labelSingular); upsertStepFilterSettings({ stepFilterToUpsert: { ...stepFilter, stepOutputKey: variableName, displayValue: isFullRecord - ? objectMetadataItem.labelSingular + ? filterObjectMetadataItem.labelSingular : (variableLabel ?? ''), type: filterType ?? 'unknown', value: '', @@ -126,7 +122,8 @@ export const WorkflowStepFilterFieldSelect = ({ upsertStepFilterSettings, stepFilter, workflowVersionId, - getFilterFieldMetadataItem, + filterFieldMetadataItem, + filterObjectMetadataItem, ], ); @@ -144,13 +141,11 @@ export const WorkflowStepFilterFieldSelect = ({ const label = isSelectedFieldNotFound || !isDefined(stepFilter.displayValue) ? t`Select a field from a previous step` - : stepFilter.displayValue; + : variableLabel; const dropdownId = `step-filter-field-${stepFilter.id}`; - const isReadonly = readonly ?? false; - - if (isReadonly || noAvailableVariables) { + if (noAvailableVariables) { return ( + } + dropdownComponents={[]} + /> + ); + } + + if (readonly === true) { + return ( + @@ -174,12 +188,14 @@ export const WorkflowStepFilterFieldSelect = ({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx index 0a5bc7892e..c9a3dbe16f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueInput.tsx @@ -7,7 +7,7 @@ import { FormTextFieldInput } from '@/object-record/record-field/ui/form-types/c import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata'; import { WorkflowStepFilterValueCompositeInput } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterValueCompositeInput'; -import { useGetFilterFieldMetadataItem } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useGetFilterFieldMetadataItem'; +import { useFilterFieldMetadataItem } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterFieldMetadataItem'; import { useUpsertStepFilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useUpsertStepFilterSettings'; import { WorkflowStepFilterContext } from '@/workflow/workflow-steps/workflow-actions/filter-action/states/context/WorkflowStepFilterContext'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; @@ -62,7 +62,6 @@ export const WorkflowStepFilterValueInput = ({ const { readonly } = useContext(WorkflowStepFilterContext); const { upsertStepFilterSettings } = useUpsertStepFilterSettings(); - const { getFilterFieldMetadataItem } = useGetFilterFieldMetadataItem(); const handleValueChange = (value: JsonValue) => { const valueToUpsert = isString(value) @@ -85,10 +84,6 @@ export const WorkflowStepFilterValueInput = ({ (stepFilter && !configurableViewFilterOperands.has(stepFilter.operand)) ?? true; - if (isDisabled || operandHasNoInput) { - return null; - } - const { fieldMetadataId, type: variableType, @@ -96,12 +91,11 @@ export const WorkflowStepFilterValueInput = ({ } = stepFilter; const { fieldMetadataItem: selectedFieldMetadataItem, objectMetadataItem } = - isDefined(fieldMetadataId) - ? getFilterFieldMetadataItem(fieldMetadataId) - : { - fieldMetadataItem: undefined, - objectMetadataItem: undefined, - }; + useFilterFieldMetadataItem(fieldMetadataId ?? ''); + + if (isDisabled || operandHasNoInput) { + return null; + } const isFilterableByMultiSelectValue = variableType === FieldMetadataType.MULTI_SELECT || diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterFieldMetadataItem.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterFieldMetadataItem.ts new file mode 100644 index 0000000000..8f3204ea22 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterFieldMetadataItem.ts @@ -0,0 +1,18 @@ +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; + +export const useFilterFieldMetadataItem = (fieldMetadataId: string) => { + const { objectMetadataItems } = useObjectMetadataItems(); + + const objectMetadataItem = objectMetadataItems.find((objectMetadataItem) => + objectMetadataItem.fields.some((field) => field.id === fieldMetadataId), + ); + + const fieldMetadataItem = objectMetadataItem?.fields.find( + (field) => field.id === fieldMetadataId, + ); + + return { + objectMetadataItem, + fieldMetadataItem, + }; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/hooks/useGetFilterFieldMetadataItem.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/hooks/useGetFilterFieldMetadataItem.ts deleted file mode 100644 index dff4240032..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/hooks/useGetFilterFieldMetadataItem.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; -import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { isDefined } from 'twenty-shared/utils'; - -export const useGetFilterFieldMetadataItem = () => { - const { objectMetadataItems } = useObjectMetadataItems(); - - const getFilterFieldMetadataItem = ( - fieldMetadataId: string, - ): { - fieldMetadataItem: FieldMetadataItem | undefined; - objectMetadataItem: ObjectMetadataItem | undefined; - } => { - for (const objectMetadataItem of objectMetadataItems) { - const field = objectMetadataItem.fields.find( - (field) => field.id === fieldMetadataId, - ); - if (isDefined(field)) { - return { - fieldMetadataItem: field, - objectMetadataItem: objectMetadataItem, - }; - } - } - return { - fieldMetadataItem: undefined, - objectMetadataItem: undefined, - }; - }; - - return { - getFilterFieldMetadataItem, - }; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx index e300ddcad4..1520df5fd0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx @@ -62,17 +62,17 @@ export const WorkflowVariablesDropdownFieldItems = ({ /> - {filteredOptions.map(([key, subStep]) => ( + {filteredOptions.map(([key, option]) => ( handleSelectField(key)} - text={subStep.label || key} - hasSubMenu={!subStep.isLeaf} - LeftIcon={subStep.icon ? getIcon(subStep.icon) : undefined} + text={option.label || key} + hasSubMenu={!option.isLeaf} + LeftIcon={option.icon ? getIcon(option.icon) : undefined} contextualText={ - subStep.isLeaf ? subStep?.value?.toString() : undefined + option.isLeaf ? option?.value?.toString() : undefined } /> ))} diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts index fa26a86c9c..4e345ec42c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts @@ -33,6 +33,7 @@ type UseVariableDropdownReturn = { setSearchInputValue: (value: string) => void; handleSelectField: (key: string) => void; goBack: () => void; + // TODO: fix typing here filteredOptions: [string, any][]; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts index 8a30d63a1a..48cc49fe11 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/types/StepOutputSchema.ts @@ -31,6 +31,10 @@ type Link = { export type BaseOutputSchema = Record; +export type FieldOutputSchema = (Leaf | Node) & { + fieldMetadataId: string; +}; + export type RecordOutputSchema = { object: { nameSingular: string; @@ -38,7 +42,7 @@ export type RecordOutputSchema = { objectMetadataId: string; isRelationField?: boolean; } & Leaf; - fields: BaseOutputSchema; + fields: Record; _outputSchemaType: 'RECORD'; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts index 1b689d3930..44e6b1fcec 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getCurrentSubStepFromPath.test.ts @@ -19,7 +19,12 @@ const mockStep = { objectMetadataId: '123', }, fields: { - name: { label: 'Name', value: 'Twenty', isLeaf: true }, + name: { + label: 'Name', + value: 'Twenty', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, }, _outputSchemaType: 'RECORD', }, diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getStepHeaderLabel.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getStepHeaderLabel.test.ts index 0af0ad7f79..c8d56dfe38 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getStepHeaderLabel.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/getStepHeaderLabel.test.ts @@ -19,16 +19,42 @@ const mockStep = { objectMetadataId: '123', }, fields: { - name: { label: 'Name', value: 'Twenty', isLeaf: true }, + name: { + label: 'Name', + value: 'Twenty', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174001', + }, address: { - label: 'Address', - value: { - street: { label: 'Street', value: '123 Main St', isLeaf: true }, - city: { label: 'City', value: 'New York', isLeaf: true }, - state: { label: 'State', value: 'NY', isLeaf: true }, - zip: { label: 'Zip', value: '10001', isLeaf: true }, - }, isLeaf: false, + label: 'Address', + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + value: { + street: { + label: 'Street', + value: '123 Main St', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, + city: { + label: 'City', + value: 'New York', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, + state: { + label: 'State', + value: 'NY', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, + zip: { + label: 'Zip', + value: '10001', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, + }, }, }, _outputSchemaType: 'RECORD', diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/searchVariableThroughOutputSchema.test.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/searchVariableThroughOutputSchema.test.ts index 26fabd45fd..590776f251 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/searchVariableThroughOutputSchema.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/__tests__/searchVariableThroughOutputSchema.test.ts @@ -22,8 +22,18 @@ describe('searchVariableThroughOutputSchema', () => { objectMetadataId: '123', }, fields: { - name: { label: 'Name', value: 'Twenty', isLeaf: true }, - address: { label: 'Address', value: '123 Main St', isLeaf: true }, + name: { + label: 'Name', + value: 'Twenty', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, + address: { + label: 'Address', + value: '123 Main St', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, }, _outputSchemaType: 'RECORD', }, @@ -42,12 +52,23 @@ describe('searchVariableThroughOutputSchema', () => { objectMetadataId: '123', }, fields: { - firstName: { label: 'First Name', value: 'Jane', isLeaf: true }, - lastName: { label: 'Last Name', value: 'Doe', isLeaf: true }, + firstName: { + label: 'First Name', + value: 'Jane', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, + lastName: { + label: 'Last Name', + value: 'Doe', + isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + }, email: { label: 'Email', value: 'jane@example.com', isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', }, }, _outputSchemaType: 'RECORD', @@ -90,6 +111,8 @@ describe('searchVariableThroughOutputSchema', () => { }); expect(result).toEqual({ + compositeFieldSubFieldName: undefined, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', variableLabel: 'Name', variablePathLabel: 'Step 1 > Company > Name', variableType: 'unknown', @@ -104,6 +127,8 @@ describe('searchVariableThroughOutputSchema', () => { }); expect(result).toEqual({ + compositeFieldSubFieldName: undefined, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', variableLabel: 'Email', variablePathLabel: 'Step 1 > Person > Email', variableType: 'unknown', @@ -237,6 +262,7 @@ describe('searchVariableThroughOutputSchema', () => { label: 'Id', value: '123e4567-e89b-12d3-a456-426614174000', isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', }, 'properties.after.name': { icon: 'IconBuildingSkyscraper', @@ -244,22 +270,28 @@ describe('searchVariableThroughOutputSchema', () => { label: 'Name', value: 'My text', isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', }, 'properties.after.annualRecurringRevenue': { icon: 'IconMoneybag', label: 'ARR', + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', value: { amountMicros: { type: FieldMetadataType.NUMERIC, label: ' Amount Micros', value: null, isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + isCompositeSubField: true, }, currencyCode: { type: FieldMetadataType.TEXT, label: ' Currency Code', value: 'My text', isLeaf: true, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', + isCompositeSubField: true, }, }, isLeaf: false, @@ -285,6 +317,8 @@ describe('searchVariableThroughOutputSchema', () => { }); expect(result).toEqual({ + compositeFieldSubFieldName: undefined, + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', variableLabel: 'Name', variablePathLabel: 'Record is Created > Name', variableType: FieldMetadataType.TEXT, @@ -300,6 +334,8 @@ describe('searchVariableThroughOutputSchema', () => { }); expect(result).toEqual({ + compositeFieldSubFieldName: 'amountMicros', + fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', variableLabel: ' Amount Micros', variablePathLabel: 'Record is Created > ARR > Amount Micros', variableType: FieldMetadataType.NUMERIC, diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts index 27da693603..8570bdaed9 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/utils/filterOutputSchema.ts @@ -1,6 +1,7 @@ import { type InputSchemaPropertyType } from '@/workflow/types/InputSchema'; import { type BaseOutputSchema, + type FieldOutputSchema, type OutputSchema, type RecordOutputSchema, } from '@/workflow/workflow-variables/types/StepOutputSchema'; @@ -35,7 +36,7 @@ const filterRecordOutputSchema = ({ shouldDisplayRecordFields: boolean; shouldDisplayRecordObjects: boolean; }): RecordOutputSchema | undefined => { - const filteredFields: BaseOutputSchema = {}; + const filteredFields: Record = {}; let hasValidFields = false; for (const key in outputSchema.fields) { @@ -135,8 +136,8 @@ const filterRecordOutputSchemaFieldsByType = ({ }: { outputSchema: RecordOutputSchema; fieldTypesToExclude: InputSchemaPropertyType[]; -}) => { - const filteredFields: BaseOutputSchema = {}; +}): RecordOutputSchema => { + const filteredFields: Record = {}; for (const key in outputSchema.fields) { const field = outputSchema.fields[key]; diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/workflow-data-seeds.constants.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/workflow-data-seeds.constants.ts deleted file mode 100644 index e4071346ae..0000000000 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/constants/workflow-data-seeds.constants.ts +++ /dev/null @@ -1,991 +0,0 @@ -import { WorkflowStatus } from 'src/modules/workflow/common/standard-objects/workflow.workspace-entity'; -import { WorkflowVersionStatus } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; -import { WORKSPACE_MEMBER_DATA_SEED_IDS } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant'; - -type WorkflowDataSeed = { - id: string; - name: string; - lastPublishedVersionId: string; - statuses: WorkflowStatus[]; - position: number; - createdBySource: string; - createdByWorkspaceMemberId: string; - createdByName: string; - createdByContext: object; -}; - -type WorkflowVersionDataSeed = { - id: string; - name: string; - trigger: string; - steps: string; - status: WorkflowVersionStatus; - position: number; - workflowId: string; -}; - -export const WORKFLOW_DATA_SEED_COLUMNS: (keyof WorkflowDataSeed)[] = [ - 'id', - 'name', - 'lastPublishedVersionId', - 'statuses', - 'position', - 'createdBySource', - 'createdByWorkspaceMemberId', - 'createdByName', - 'createdByContext', -]; - -export const WORKFLOW_VERSION_DATA_SEED_COLUMNS: (keyof WorkflowVersionDataSeed)[] = - ['id', 'name', 'trigger', 'steps', 'status', 'position', 'workflowId']; - -export const WORKFLOW_DATA_SEED_IDS = { - ID_1: '20202020-8532-47bd-a4fe-2c1e5c4aeed3', -}; - -export const WORKFLOW_VERSION_DATA_SEED_IDS = { - ID_1: '20202020-9cd5-44a8-8b2f-1160b01f06dd', -}; - -export const WORKFLOW_DATA_SEEDS: WorkflowDataSeed[] = [ - { - id: WORKFLOW_DATA_SEED_IDS.ID_1, - name: 'Quick Lead', - lastPublishedVersionId: WORKFLOW_VERSION_DATA_SEED_IDS.ID_1, - statuses: [WorkflowStatus.ACTIVE], - position: 1, - createdBySource: 'MANUAL', - createdByWorkspaceMemberId: WORKSPACE_MEMBER_DATA_SEED_IDS.TIM, - createdByName: 'Tim A', - createdByContext: {}, - }, -]; - -export const WORKFLOW_VERSION_DATA_SEEDS: WorkflowVersionDataSeed[] = [ - { - id: WORKFLOW_VERSION_DATA_SEED_IDS.ID_1, - name: 'v1', - trigger: JSON.stringify({ - name: 'Launch manually', - type: 'MANUAL', - position: { - x: 0, - y: 0, - }, - settings: { outputSchema: {} }, - nextStepIds: ['6e089bc9-aabd-435f-865f-f31c01c8f4a7'], - }), - steps: JSON.stringify([ - { - id: '6e089bc9-aabd-435f-865f-f31c01c8f4a7', - name: 'Quick Lead Form', - type: 'FORM', - valid: false, - position: { - x: 0, - y: 141, - }, - settings: { - input: [ - { - id: '14d669f0-5249-4fa4-b0bb-f8bd408328d5', - name: 'firstName', - type: 'TEXT', - label: 'First name', - placeholder: 'Tim', - }, - { - id: '4eb6ce85-d231-4aef-9837-744490c026d0', - name: 'lastName', - type: 'TEXT', - label: 'Last Name', - placeholder: 'Apple', - }, - { - id: 'adbf0e9f-1427-49be-b4fb-092b34d97350', - name: 'email', - type: 'TEXT', - label: 'Email', - placeholder: 'timapple@apple.com', - }, - { - id: '4ffc7992-9e65-4a4d-9baf-b52e62f2c273', - name: 'jobTitle', - type: 'TEXT', - label: 'Job title', - placeholder: 'CEO', - }, - { - id: '42f11926-04ea-4924-94a4-2293cc748362', - name: 'companyName', - type: 'TEXT', - label: 'Company name', - placeholder: 'Apple', - }, - { - id: 'd6ca80ee-26cd-466d-91bf-984d7205451c', - name: 'companyDomain', - type: 'TEXT', - label: 'Company domain', - placeholder: 'https://www.apple.com', - }, - ], - outputSchema: { - email: { - type: 'TEXT', - label: 'Email', - value: 'My text', - isLeaf: true, - }, - jobTitle: { - type: 'TEXT', - label: 'Job title', - value: 'My text', - isLeaf: true, - }, - lastName: { - type: 'TEXT', - label: 'Last Name', - value: 'My text', - isLeaf: true, - }, - firstName: { - type: 'TEXT', - label: 'First name', - value: 'My text', - isLeaf: true, - }, - companyName: { - type: 'TEXT', - label: 'Company name', - value: 'My text', - isLeaf: true, - }, - companyDomain: { - type: 'TEXT', - label: 'Company domain', - value: 'My text', - isLeaf: true, - }, - }, - errorHandlingOptions: { - retryOnFailure: { value: false }, - continueOnFailure: { value: false }, - }, - }, - __typename: 'WorkflowAction', - nextStepIds: ['0715b6cd-7cc1-4b98-971b-00f54dfe643b'], - }, - { - id: '0715b6cd-7cc1-4b98-971b-00f54dfe643b', - name: 'Create Company', - type: 'CREATE_RECORD', - valid: false, - position: { - x: 0.5, - y: 282, - }, - settings: { - input: { - objectName: 'company', - objectRecord: { - name: '{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.companyName}}', - domainName: { - primaryLinkUrl: - '{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.companyDomain}}', - primaryLinkLabel: '', - }, - }, - }, - outputSchema: { - fields: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - name: { - icon: 'IconBuildingSkyscraper', - type: 'TEXT', - label: 'Name', - value: 'My text', - isLeaf: true, - }, - xLink: { - icon: 'IconBrandX', - label: 'X', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - address: { - icon: 'IconMap', - label: 'Address', - value: { - addressLat: { - type: 'NUMERIC', - label: ' Address Lat', - value: null, - isLeaf: true, - }, - addressLng: { - type: 'NUMERIC', - label: ' Address Lng', - value: null, - isLeaf: true, - }, - addressCity: { - type: 'TEXT', - label: ' Address City', - value: 'My text', - isLeaf: true, - }, - addressState: { - type: 'TEXT', - label: ' Address State', - value: 'My text', - isLeaf: true, - }, - addressCountry: { - type: 'TEXT', - label: ' Address Country', - value: 'My text', - isLeaf: true, - }, - addressStreet1: { - type: 'TEXT', - label: ' Address Street1', - value: 'My text', - isLeaf: true, - }, - addressStreet2: { - type: 'TEXT', - label: ' Address Street2', - value: 'My text', - isLeaf: true, - }, - addressPostcode: { - type: 'TEXT', - label: ' Address Postcode', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - createdBy: { - icon: 'IconCreativeCommonsSa', - label: 'Created by', - value: { - name: { - type: 'TEXT', - label: ' Name', - value: 'My text', - isLeaf: true, - }, - source: { - type: 'SELECT', - label: ' Source', - value: null, - isLeaf: true, - }, - context: { - type: 'RAW_JSON', - label: ' Context', - value: null, - isLeaf: true, - }, - workspaceMemberId: { - type: 'UUID', - label: ' Workspace Member Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - }, - isLeaf: false, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - employees: { - icon: 'IconUsers', - type: 'NUMBER', - label: 'Employees', - value: 20, - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - domainName: { - icon: 'IconLink', - label: 'Domain Name', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - accountOwner: { - icon: 'IconUserCircle', - label: 'Account Owner', - value: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - name: { - icon: 'IconCircleUser', - label: 'Name', - value: { - lastName: { - type: 'TEXT', - label: ' Last Name', - value: 'My text', - isLeaf: true, - }, - firstName: { - type: 'TEXT', - label: ' First Name', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - userEmail: { - icon: 'IconMail', - type: 'TEXT', - label: 'User Email', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - linkedinLink: { - icon: 'IconBrandLinkedin', - label: 'Linkedin', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - idealCustomerProfile: { - icon: 'IconTarget', - type: 'BOOLEAN', - label: 'ICP', - value: true, - isLeaf: true, - }, - annualRecurringRevenue: { - icon: 'IconMoneybag', - label: 'ARR', - value: { - amountMicros: { - type: 'NUMERIC', - label: ' Amount Micros', - value: null, - isLeaf: true, - }, - currencyCode: { - type: 'TEXT', - label: ' Currency Code', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - }, - object: { - icon: 'IconBuildingSkyscraper', - label: 'Company', - value: 'A company', - isLeaf: true, - fieldIdName: 'id', - nameSingular: 'company', - }, - _outputSchemaType: 'RECORD', - }, - errorHandlingOptions: { - retryOnFailure: { value: false }, - continueOnFailure: { value: false }, - }, - }, - __typename: 'WorkflowAction', - nextStepIds: ['6f553ea7-b00e-4371-9d88-d8298568a246'], - }, - { - id: '6f553ea7-b00e-4371-9d88-d8298568a246', - name: 'Create Person', - type: 'CREATE_RECORD', - valid: false, - position: { - x: 8.5, - y: 423, - }, - settings: { - input: { - objectName: 'person', - objectRecord: { - name: { - lastName: '{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.lastName}}', - firstName: '{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.firstName}}', - }, - emails: { - primaryEmail: '{{6e089bc9-aabd-435f-865f-f31c01c8f4a7.email}}', - additionalEmails: [], - }, - company: { - id: '{{0715b6cd-7cc1-4b98-971b-00f54dfe643b.id}}', - }, - }, - }, - outputSchema: { - fields: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - city: { - icon: 'IconMap', - type: 'TEXT', - label: 'City', - value: 'My text', - isLeaf: true, - }, - name: { - icon: 'IconUser', - label: 'Name', - value: { - lastName: { - type: 'TEXT', - label: ' Last Name', - value: 'My text', - isLeaf: true, - }, - firstName: { - type: 'TEXT', - label: ' First Name', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - xLink: { - icon: 'IconBrandX', - label: 'X', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - emails: { - icon: 'IconMail', - label: 'Emails', - value: { - primaryEmail: { - type: 'TEXT', - label: ' Primary Email', - value: 'My text', - isLeaf: true, - }, - additionalEmails: { - type: 'RAW_JSON', - label: ' Additional Emails', - value: null, - isLeaf: true, - }, - }, - isLeaf: false, - }, - phones: { - icon: 'IconPhone', - label: 'Phones', - value: { - additionalPhones: { - type: 'RAW_JSON', - label: ' Additional Phones', - value: null, - isLeaf: true, - }, - primaryPhoneNumber: { - type: 'TEXT', - label: ' Primary Phone Number', - value: 'My text', - isLeaf: true, - }, - primaryPhoneCallingCode: { - type: 'TEXT', - label: ' Primary Phone Calling Code', - value: 'My text', - isLeaf: true, - }, - primaryPhoneCountryCode: { - type: 'TEXT', - label: ' Primary Phone Country Code', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - company: { - icon: 'IconBuildingSkyscraper', - label: 'Company', - value: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - name: { - icon: 'IconBuildingSkyscraper', - type: 'TEXT', - label: 'Name', - value: 'My text', - isLeaf: true, - }, - xLink: { - icon: 'IconBrandX', - label: 'X', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - address: { - icon: 'IconMap', - label: 'Address', - value: { - addressLat: { - type: 'NUMERIC', - label: ' Address Lat', - value: null, - isLeaf: true, - }, - addressLng: { - type: 'NUMERIC', - label: ' Address Lng', - value: null, - isLeaf: true, - }, - addressCity: { - type: 'TEXT', - label: ' Address City', - value: 'My text', - isLeaf: true, - }, - addressState: { - type: 'TEXT', - label: ' Address State', - value: 'My text', - isLeaf: true, - }, - addressCountry: { - type: 'TEXT', - label: ' Address Country', - value: 'My text', - isLeaf: true, - }, - addressStreet1: { - type: 'TEXT', - label: ' Address Street1', - value: 'My text', - isLeaf: true, - }, - addressStreet2: { - type: 'TEXT', - label: ' Address Street2', - value: 'My text', - isLeaf: true, - }, - addressPostcode: { - type: 'TEXT', - label: ' Address Postcode', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - createdBy: { - icon: 'IconCreativeCommonsSa', - label: 'Created by', - value: { - name: { - type: 'TEXT', - label: ' Name', - value: 'My text', - isLeaf: true, - }, - source: { - type: 'SELECT', - label: ' Source', - value: null, - isLeaf: true, - }, - context: { - type: 'RAW_JSON', - label: ' Context', - value: null, - isLeaf: true, - }, - workspaceMemberId: { - type: 'UUID', - label: ' Workspace Member Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - }, - isLeaf: false, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - employees: { - icon: 'IconUsers', - type: 'NUMBER', - label: 'Employees', - value: 20, - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - domainName: { - icon: 'IconLink', - label: 'Domain Name', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - linkedinLink: { - icon: 'IconBrandLinkedin', - label: 'Linkedin', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - idealCustomerProfile: { - icon: 'IconTarget', - type: 'BOOLEAN', - label: 'ICP', - value: true, - isLeaf: true, - }, - annualRecurringRevenue: { - icon: 'IconMoneybag', - label: 'ARR', - value: { - amountMicros: { - type: 'NUMERIC', - label: ' Amount Micros', - value: null, - isLeaf: true, - }, - currencyCode: { - type: 'TEXT', - label: ' Currency Code', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - }, - isLeaf: false, - }, - jobTitle: { - icon: 'IconBriefcase', - type: 'TEXT', - label: 'Job Title', - value: 'My text', - isLeaf: true, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - createdBy: { - icon: 'IconCreativeCommonsSa', - label: 'Created by', - value: { - name: { - type: 'TEXT', - label: ' Name', - value: 'My text', - isLeaf: true, - }, - source: { - type: 'SELECT', - label: ' Source', - value: null, - isLeaf: true, - }, - context: { - type: 'RAW_JSON', - label: ' Context', - value: null, - isLeaf: true, - }, - workspaceMemberId: { - type: 'UUID', - label: ' Workspace Member Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - }, - isLeaf: false, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - linkedinLink: { - icon: 'IconBrandLinkedin', - label: 'Linkedin', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - }, - object: { - icon: 'IconUser', - label: 'Person', - value: 'A person', - isLeaf: true, - fieldIdName: 'id', - nameSingular: 'person', - }, - _outputSchemaType: 'RECORD', - }, - errorHandlingOptions: { - retryOnFailure: { value: false }, - continueOnFailure: { value: false }, - }, - }, - __typename: 'WorkflowAction', - nextStepIds: null, - }, - ]), - status: WorkflowVersionStatus.ACTIVE, - position: 1, - workflowId: WORKFLOW_DATA_SEED_IDS.ID_1, - }, -]; diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service.ts index fc41cc102a..9e38bf37d2 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/data/services/dev-seeder-data.service.ts @@ -81,18 +81,13 @@ import { TASK_TARGET_DATA_SEED_COLUMNS, TASK_TARGET_DATA_SEEDS, } from 'src/engine/workspace-manager/dev-seeder/data/constants/task-target-data-seeds.constant'; -import { - WORKFLOW_DATA_SEED_COLUMNS, - WORKFLOW_DATA_SEEDS, - WORKFLOW_VERSION_DATA_SEED_COLUMNS, - WORKFLOW_VERSION_DATA_SEEDS, -} from 'src/engine/workspace-manager/dev-seeder/data/constants/workflow-data-seeds.constants'; import { WORKSPACE_MEMBER_DATA_SEED_COLUMNS, WORKSPACE_MEMBER_DATA_SEEDS, } from 'src/engine/workspace-manager/dev-seeder/data/constants/workspace-member-data-seeds.constant'; import { TimelineActivitySeederService } from 'src/engine/workspace-manager/dev-seeder/data/services/timeline-activity-seeder.service'; import { prefillViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-views'; +import { prefillWorkflows } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows'; import { prefillWorkspaceFavorites } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workspace-favorites'; const RECORD_SEEDS_CONFIGS = [ @@ -176,16 +171,6 @@ const RECORD_SEEDS_CONFIGS = [ pgColumns: MESSAGE_PARTICIPANT_DATA_SEED_COLUMNS, recordSeeds: MESSAGE_PARTICIPANT_DATA_SEEDS, }, - { - tableName: 'workflow', - pgColumns: WORKFLOW_DATA_SEED_COLUMNS, - recordSeeds: WORKFLOW_DATA_SEEDS, - }, - { - tableName: 'workflowVersion', - pgColumns: WORKFLOW_VERSION_DATA_SEED_COLUMNS, - recordSeeds: WORKFLOW_VERSION_DATA_SEEDS, - }, { tableName: '_pet', pgColumns: PET_DATA_SEED_COLUMNS, @@ -261,16 +246,14 @@ export class DevSeederDataService { workspaceId, }); - // For now views/favorites are auto-created for custom - // objects but not for standard objects. - // This is probably something we want to fix in the future. - const viewDefinitionsWithId = await prefillViews( entityManager, schemaName, objectMetadataItems.filter((item) => !item.isCustom), ); + await prefillWorkflows(entityManager, schemaName, objectMetadataItems); + await prefillWorkspaceFavorites( viewDefinitionsWithId .filter( diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts index 5da79643fd..6101b9fcf7 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows.ts @@ -1,5 +1,10 @@ +import { isDefined } from 'twenty-shared/utils'; + import { FieldActorSource } from 'src/engine/metadata-modules/field-metadata/composite-types/actor.composite-type'; +import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; +import { generateObjectMetadataMaps } from 'src/engine/metadata-modules/utils/generate-object-metadata-maps.util'; import { type WorkspaceEntityManager } from 'src/engine/twenty-orm/entity-manager/workspace-entity-manager'; +import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields'; const QUICK_LEAD_WORKFLOW_ID = '8b213cac-a68b-4ffe-817a-3ec994e9932d'; const QUICK_LEAD_WORKFLOW_VERSION_ID = 'ac67974f-c524-4288-9d88-af8515400b68'; @@ -7,7 +12,30 @@ const QUICK_LEAD_WORKFLOW_VERSION_ID = 'ac67974f-c524-4288-9d88-af8515400b68'; export const prefillWorkflows = async ( entityManager: WorkspaceEntityManager, schemaName: string, + objectMetadataItems: ObjectMetadataEntity[], ) => { + const objectMetadataMaps = generateObjectMetadataMaps(objectMetadataItems); + const companyObjectMetadataId = + objectMetadataMaps.idByNameSingular['company']; + + const personObjectMetadataId = objectMetadataMaps.idByNameSingular['person']; + + if ( + !isDefined(companyObjectMetadataId) || + !isDefined(personObjectMetadataId) + ) { + throw new Error('Company or person object metadata not found'); + } + + const companyObjectMetadata = + objectMetadataMaps.byId[companyObjectMetadataId]; + + const personObjectMetadata = objectMetadataMaps.byId[personObjectMetadataId]; + + if (!isDefined(companyObjectMetadata) || !isDefined(personObjectMetadata)) { + throw new Error('Company or person object metadata not found'); + } + await entityManager .createQueryBuilder(undefined, undefined, undefined, { shouldBypassPermissionChecks: true, @@ -181,298 +209,6 @@ export const prefillWorkflows = async ( }, }, outputSchema: { - fields: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - name: { - icon: 'IconBuildingSkyscraper', - type: 'TEXT', - label: 'Name', - value: 'My text', - isLeaf: true, - }, - xLink: { - icon: 'IconBrandX', - label: 'X', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - address: { - icon: 'IconMap', - label: 'Address', - value: { - addressLat: { - type: 'NUMERIC', - label: ' Address Lat', - value: null, - isLeaf: true, - }, - addressLng: { - type: 'NUMERIC', - label: ' Address Lng', - value: null, - isLeaf: true, - }, - addressCity: { - type: 'TEXT', - label: ' Address City', - value: 'My text', - isLeaf: true, - }, - addressState: { - type: 'TEXT', - label: ' Address State', - value: 'My text', - isLeaf: true, - }, - addressCountry: { - type: 'TEXT', - label: ' Address Country', - value: 'My text', - isLeaf: true, - }, - addressStreet1: { - type: 'TEXT', - label: ' Address Street1', - value: 'My text', - isLeaf: true, - }, - addressStreet2: { - type: 'TEXT', - label: ' Address Street2', - value: 'My text', - isLeaf: true, - }, - addressPostcode: { - type: 'TEXT', - label: ' Address Postcode', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - createdBy: { - icon: 'IconCreativeCommonsSa', - label: 'Created by', - value: { - name: { - type: 'TEXT', - label: ' Name', - value: 'My text', - isLeaf: true, - }, - source: { - type: 'SELECT', - label: ' Source', - value: null, - isLeaf: true, - }, - context: { - type: 'RAW_JSON', - label: ' Context', - value: null, - isLeaf: true, - }, - workspaceMemberId: { - type: 'UUID', - label: ' Workspace Member Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - }, - isLeaf: false, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - employees: { - icon: 'IconUsers', - type: 'NUMBER', - label: 'Employees', - value: 20, - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - domainName: { - icon: 'IconLink', - label: 'Domain Name', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - accountOwner: { - icon: 'IconUserCircle', - label: 'Account Owner', - value: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - name: { - icon: 'IconCircleUser', - label: 'Name', - value: { - lastName: { - type: 'TEXT', - label: ' Last Name', - value: 'My text', - isLeaf: true, - }, - firstName: { - type: 'TEXT', - label: ' First Name', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - userEmail: { - icon: 'IconMail', - type: 'TEXT', - label: 'User Email', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - linkedinLink: { - icon: 'IconBrandLinkedin', - label: 'Linkedin', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - idealCustomerProfile: { - icon: 'IconTarget', - type: 'BOOLEAN', - label: 'ICP', - value: true, - isLeaf: true, - }, - annualRecurringRevenue: { - icon: 'IconMoneybag', - label: 'ARR', - value: { - amountMicros: { - type: 'NUMERIC', - label: ' Amount Micros', - value: null, - isLeaf: true, - }, - currencyCode: { - type: 'TEXT', - label: ' Currency Code', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - }, object: { icon: 'IconBuildingSkyscraper', label: 'Company', @@ -482,6 +218,13 @@ export const prefillWorkflows = async ( nameSingular: 'company', }, _outputSchemaType: 'RECORD', + fields: generateObjectRecordFields({ + objectMetadataInfo: { + objectMetadataItemWithFieldsMaps: companyObjectMetadata, + objectMetadataMaps, + }, + depth: 0, + }), }, errorHandlingOptions: { retryOnFailure: { value: false }, @@ -517,445 +260,13 @@ export const prefillWorkflows = async ( }, }, outputSchema: { - fields: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, + fields: generateObjectRecordFields({ + objectMetadataInfo: { + objectMetadataItemWithFieldsMaps: personObjectMetadata, + objectMetadataMaps, }, - city: { - icon: 'IconMap', - type: 'TEXT', - label: 'City', - value: 'My text', - isLeaf: true, - }, - name: { - icon: 'IconUser', - label: 'Name', - value: { - lastName: { - type: 'TEXT', - label: ' Last Name', - value: 'My text', - isLeaf: true, - }, - firstName: { - type: 'TEXT', - label: ' First Name', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - xLink: { - icon: 'IconBrandX', - label: 'X', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - emails: { - icon: 'IconMail', - label: 'Emails', - value: { - primaryEmail: { - type: 'TEXT', - label: ' Primary Email', - value: 'My text', - isLeaf: true, - }, - additionalEmails: { - type: 'RAW_JSON', - label: ' Additional Emails', - value: null, - isLeaf: true, - }, - }, - isLeaf: false, - }, - phones: { - icon: 'IconPhone', - label: 'Phones', - value: { - additionalPhones: { - type: 'RAW_JSON', - label: ' Additional Phones', - value: null, - isLeaf: true, - }, - primaryPhoneNumber: { - type: 'TEXT', - label: ' Primary Phone Number', - value: 'My text', - isLeaf: true, - }, - primaryPhoneCallingCode: { - type: 'TEXT', - label: ' Primary Phone Calling Code', - value: 'My text', - isLeaf: true, - }, - primaryPhoneCountryCode: { - type: 'TEXT', - label: ' Primary Phone Country Code', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - company: { - icon: 'IconBuildingSkyscraper', - label: 'Company', - value: { - id: { - icon: 'Icon123', - type: 'UUID', - label: 'Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - name: { - icon: 'IconBuildingSkyscraper', - type: 'TEXT', - label: 'Name', - value: 'My text', - isLeaf: true, - }, - xLink: { - icon: 'IconBrandX', - label: 'X', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - address: { - icon: 'IconMap', - label: 'Address', - value: { - addressLat: { - type: 'NUMERIC', - label: ' Address Lat', - value: null, - isLeaf: true, - }, - addressLng: { - type: 'NUMERIC', - label: ' Address Lng', - value: null, - isLeaf: true, - }, - addressCity: { - type: 'TEXT', - label: ' Address City', - value: 'My text', - isLeaf: true, - }, - addressState: { - type: 'TEXT', - label: ' Address State', - value: 'My text', - isLeaf: true, - }, - addressCountry: { - type: 'TEXT', - label: ' Address Country', - value: 'My text', - isLeaf: true, - }, - addressStreet1: { - type: 'TEXT', - label: ' Address Street1', - value: 'My text', - isLeaf: true, - }, - addressStreet2: { - type: 'TEXT', - label: ' Address Street2', - value: 'My text', - isLeaf: true, - }, - addressPostcode: { - type: 'TEXT', - label: ' Address Postcode', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - createdBy: { - icon: 'IconCreativeCommonsSa', - label: 'Created by', - value: { - name: { - type: 'TEXT', - label: ' Name', - value: 'My text', - isLeaf: true, - }, - source: { - type: 'SELECT', - label: ' Source', - value: null, - isLeaf: true, - }, - context: { - type: 'RAW_JSON', - label: ' Context', - value: null, - isLeaf: true, - }, - workspaceMemberId: { - type: 'UUID', - label: ' Workspace Member Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - }, - isLeaf: false, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - employees: { - icon: 'IconUsers', - type: 'NUMBER', - label: 'Employees', - value: 20, - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - domainName: { - icon: 'IconLink', - label: 'Domain Name', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - linkedinLink: { - icon: 'IconBrandLinkedin', - label: 'Linkedin', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - idealCustomerProfile: { - icon: 'IconTarget', - type: 'BOOLEAN', - label: 'ICP', - value: true, - isLeaf: true, - }, - annualRecurringRevenue: { - icon: 'IconMoneybag', - label: 'ARR', - value: { - amountMicros: { - type: 'NUMERIC', - label: ' Amount Micros', - value: null, - isLeaf: true, - }, - currencyCode: { - type: 'TEXT', - label: ' Currency Code', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - }, - isLeaf: false, - }, - jobTitle: { - icon: 'IconBriefcase', - type: 'TEXT', - label: 'Job Title', - value: 'My text', - isLeaf: true, - }, - createdAt: { - icon: 'IconCalendar', - type: 'DATE_TIME', - label: 'Creation date', - value: '01/23/2025 15:16', - isLeaf: true, - }, - createdBy: { - icon: 'IconCreativeCommonsSa', - label: 'Created by', - value: { - name: { - type: 'TEXT', - label: ' Name', - value: 'My text', - isLeaf: true, - }, - source: { - type: 'SELECT', - label: ' Source', - value: null, - isLeaf: true, - }, - context: { - type: 'RAW_JSON', - label: ' Context', - value: null, - isLeaf: true, - }, - workspaceMemberId: { - type: 'UUID', - label: ' Workspace Member Id', - value: '123e4567-e89b-12d3-a456-426614174000', - isLeaf: true, - }, - }, - isLeaf: false, - }, - deletedAt: { - icon: 'IconCalendarMinus', - type: 'DATE_TIME', - label: 'Deleted at', - value: '01/23/2025 15:16', - isLeaf: true, - }, - updatedAt: { - icon: 'IconCalendarClock', - type: 'DATE_TIME', - label: 'Last update', - value: '01/23/2025 15:16', - isLeaf: true, - }, - linkedinLink: { - icon: 'IconBrandLinkedin', - label: 'Linkedin', - value: { - primaryLinkUrl: { - type: 'TEXT', - label: ' Primary Link Url', - value: 'My text', - isLeaf: true, - }, - secondaryLinks: { - type: 'RAW_JSON', - label: ' Secondary Links', - value: null, - isLeaf: true, - }, - primaryLinkLabel: { - type: 'TEXT', - label: ' Primary Link Label', - value: 'My text', - isLeaf: true, - }, - }, - isLeaf: false, - }, - }, - object: { - icon: 'IconUser', - label: 'Person', - value: 'A person', - isLeaf: true, - fieldIdName: 'id', - nameSingular: 'person', - }, - _outputSchemaType: 'RECORD', + depth: 0, + }), }, errorHandlingOptions: { retryOnFailure: { value: false }, diff --git a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts index 43e8573fed..312f595931 100644 --- a/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts +++ b/packages/twenty-server/src/engine/workspace-manager/standard-objects-prefill-data/standard-objects-prefill-data.ts @@ -6,8 +6,8 @@ import { shouldSeedWorkspaceFavorite } from 'src/engine/utils/should-seed-worksp import { prefillCompanies } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-companies'; import { prefillPeople } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-people'; import { prefillViews } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-views'; -import { prefillWorkspaceFavorites } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workspace-favorites'; import { prefillWorkflows } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workflows'; +import { prefillWorkspaceFavorites } from 'src/engine/workspace-manager/standard-objects-prefill-data/prefill-workspace-favorites'; export const standardObjectsPrefillData = async ( mainDataSource: DataSource, @@ -19,7 +19,7 @@ export const standardObjectsPrefillData = async ( await prefillPeople(entityManager, schemaName); - await prefillWorkflows(entityManager, schemaName); + await prefillWorkflows(entityManager, schemaName, objectMetadataItems); const viewDefinitionsWithId = await prefillViews( entityManager, diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts index 0f1f728179..e614b4c5a9 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type.ts @@ -31,7 +31,7 @@ export type BaseOutputSchema = Record; export type FieldOutputSchema = | ((Leaf | Node) & { - fieldMetadataId?: string; + fieldMetadataId: string; }) | RecordOutputSchema; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts index f8505604fc..3785f60fab 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-form-response.spec.ts @@ -10,7 +10,7 @@ const companyMockObjectMetadataItem = mockObjectMetadataItemsWithFieldMaps.find( describe('generateFakeFormResponse', () => { it('should generate fake responses for a form schema', async () => { - const schema: FormFieldMetadata[] = [ + const formFieldMetadataItems: FormFieldMetadata[] = [ { id: '96939213-49ac-4dee-949d-56e6c7be98e6', name: 'name', @@ -51,8 +51,8 @@ describe('generateFakeFormResponse', () => { }, }; - const result = await generateFakeFormResponse({ - formMetadata: schema, + const result = generateFakeFormResponse({ + formFieldMetadataItems, objectMetadataMaps: mockObjectMetadataMaps, }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-record-field.spec.ts similarity index 93% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-record-field.spec.ts index c153b21f0d..4f4b247257 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-field.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-fake-record-field.spec.ts @@ -1,7 +1,7 @@ import { FieldMetadataType } from 'twenty-shared/types'; import * as generateFakeValueModule from 'src/engine/utils/generate-fake-value'; -import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field'; +import { generateFakeRecordField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-record-field'; import * as camelToTitleCaseModule from 'src/utils/camel-to-title-case'; jest.mock('src/engine/utils/generate-fake-value'); @@ -52,7 +52,7 @@ describe('generateFakeField', () => { it('should generate a leaf node for TEXT type', () => { generateFakeValueSpy.mockReturnValueOnce('Fake Text'); - const result = generateFakeField({ + const result = generateFakeRecordField({ type: FieldMetadataType.TEXT, label: 'Text Field', fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', @@ -74,7 +74,7 @@ describe('generateFakeField', () => { }); it('should handle custom value', () => { - const result = generateFakeField({ + const result = generateFakeRecordField({ type: FieldMetadataType.TEXT, label: 'Text Field', value: 'Test value', @@ -96,7 +96,7 @@ describe('generateFakeField', () => { it('should generate a leaf node for NUMBER type with icon', () => { generateFakeValueSpy.mockReturnValueOnce(42); - const result = generateFakeField({ + const result = generateFakeRecordField({ type: FieldMetadataType.NUMBER, label: 'Number Field', icon: 'IconNumber', @@ -118,7 +118,7 @@ describe('generateFakeField', () => { generateFakeValueSpy.mockReturnValueOnce(fakeDate); - const result = generateFakeField({ + const result = generateFakeRecordField({ type: FieldMetadataType.DATE, label: 'Date Field', fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', @@ -145,7 +145,7 @@ describe('generateFakeField', () => { .mockReturnValueOnce('Label') .mockReturnValueOnce('Url'); - const result = generateFakeField({ + const result = generateFakeRecordField({ type: FieldMetadataType.LINKS, label: 'Links Field', fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', @@ -189,7 +189,7 @@ describe('generateFakeField', () => { .mockReturnValueOnce('Amount') .mockReturnValueOnce('Currency Code'); - const result = generateFakeField({ + const result = generateFakeRecordField({ type: FieldMetadataType.CURRENCY, label: 'Currency Field', icon: 'IconCurrency', @@ -230,7 +230,7 @@ describe('generateFakeField', () => { generateFakeValueSpy.mockReturnValueOnce('Unknown Value'); - const result = generateFakeField({ + const result = generateFakeRecordField({ type: unknownType, label: 'Unknown Field', fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', @@ -249,7 +249,7 @@ describe('generateFakeField', () => { it('should handle empty label', () => { generateFakeValueSpy.mockReturnValueOnce('Fake Boolean'); - const result = generateFakeField({ + const result = generateFakeRecordField({ type: FieldMetadataType.BOOLEAN, label: '', fieldMetadataId: '123e4567-e89b-12d3-a456-426614174000', diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-object-record-fields.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-object-record-fields.spec.ts index 1449d871ad..7c4261bfba 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-object-record-fields.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/__tests__/generate-object-record-fields.spec.ts @@ -1,12 +1,12 @@ import { FieldMetadataType } from 'twenty-shared/types'; -import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field'; +import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps'; +import { generateFakeRecordField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-record-field'; import { generateObjectRecordFields } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields'; import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value'; -import { mockObjectMetadataItemsWithFieldMaps } from 'src/engine/core-modules/__mocks__/mockObjectMetadataItemsWithFieldMaps'; jest.mock( - 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field', + 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-record-field', ); jest.mock( 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value', @@ -42,7 +42,7 @@ describe('generateObjectRecordFields', () => { (field) => field.type !== FieldMetadataType.RELATION, ); - (generateFakeField as jest.Mock).mockImplementation( + (generateFakeRecordField as jest.Mock).mockImplementation( ({ type, label, icon }) => ({ type, label, @@ -69,7 +69,7 @@ describe('generateObjectRecordFields', () => { }); expect(shouldGenerateFieldFakeValue).toHaveBeenCalledTimes(2); - expect(generateFakeField).toHaveBeenCalledTimes(2); + expect(generateFakeRecordField).toHaveBeenCalledTimes(2); }); it('should return empty object when no valid fields', () => { @@ -79,6 +79,6 @@ describe('generateObjectRecordFields', () => { expect(result).toEqual({}); expect(shouldGenerateFieldFakeValue).toHaveBeenCalledTimes(2); - expect(generateFakeField).not.toHaveBeenCalled(); + expect(generateFakeRecordField).not.toHaveBeenCalled(); }); }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-field.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-field.ts new file mode 100644 index 0000000000..a6dad16650 --- /dev/null +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-field.ts @@ -0,0 +1,26 @@ +import { type FieldMetadataType } from 'twenty-shared/types'; + +import { generateFakeValue } from 'src/engine/utils/generate-fake-value'; +import { + type Leaf, + type Node, +} from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; + +type GenerateFakeFormFieldArgs = { + type: FieldMetadataType; + label: string; + value?: string; +}; + +export const generateFakeFormField = ({ + type, + label, + value, +}: GenerateFakeFormFieldArgs): Leaf | Node => { + return { + isLeaf: true, + type: type, + label: label, + value: value ?? generateFakeValue(type, 'FieldMetadataType'), + }; +}; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts index f9a70ef8c0..c5657ebd9c 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-response.ts @@ -6,59 +6,59 @@ import { type Leaf, type Node, } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; -import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field'; +import { generateFakeFormField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-form-field'; import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; import { type FormFieldMetadata } from 'src/modules/workflow/workflow-executor/workflow-actions/form/types/workflow-form-action-settings.type'; -export const generateFakeFormResponse = async ({ - formMetadata, - objectMetadataMaps, -}: { - formMetadata: FormFieldMetadata[]; +type GenerateFakeFormResponseArgs = { + formFieldMetadataItems: FormFieldMetadata[]; objectMetadataMaps: ObjectMetadataMaps; -}): Promise> => { - const result = await Promise.all( - formMetadata.map(async (formFieldMetadata) => { - if (formFieldMetadata.type === 'RECORD') { - if (!formFieldMetadata?.settings?.objectName) { - return undefined; - } +}; - const objectMetadataItemWithFieldsMaps = - getObjectMetadataMapItemByNameSingular( - objectMetadataMaps, - formFieldMetadata?.settings?.objectName, - ); - - if (!isDefined(objectMetadataItemWithFieldsMaps)) { - throw new Error( - `Object metadata not found for object name ${formFieldMetadata?.settings?.objectName}`, - ); - } - - return { - [formFieldMetadata.name]: { - isLeaf: false, - label: formFieldMetadata.label, - value: generateFakeObjectRecord({ - objectMetadataInfo: { - objectMetadataItemWithFieldsMaps, - objectMetadataMaps, - }, - }), - }, - }; - } else { - return { - [formFieldMetadata.name]: generateFakeField({ - type: formFieldMetadata.type, - label: formFieldMetadata.label, - value: formFieldMetadata.placeholder, - }), - }; +export const generateFakeFormResponse = ({ + formFieldMetadataItems, + objectMetadataMaps, +}: GenerateFakeFormResponseArgs): Record => { + const result = formFieldMetadataItems.map((formFieldMetadata) => { + if (formFieldMetadata.type === 'RECORD') { + if (!formFieldMetadata?.settings?.objectName) { + return undefined; } - }), - ); + + const objectMetadataItemWithFieldsMaps = + getObjectMetadataMapItemByNameSingular( + objectMetadataMaps, + formFieldMetadata?.settings?.objectName, + ); + + if (!isDefined(objectMetadataItemWithFieldsMaps)) { + throw new Error( + `Object metadata not found for object name ${formFieldMetadata?.settings?.objectName}`, + ); + } + + return { + [formFieldMetadata.name]: { + isLeaf: false, + label: formFieldMetadata.label, + value: generateFakeObjectRecord({ + objectMetadataInfo: { + objectMetadataItemWithFieldsMaps, + objectMetadataMaps, + }, + }), + }, + }; + } else { + return { + [formFieldMetadata.name]: generateFakeFormField({ + type: formFieldMetadata.type, + label: formFieldMetadata.label, + value: formFieldMetadata.placeholder, + }), + }; + } + }); return result.filter(isDefined).reduce( (acc, curr) => { diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-record-field.ts similarity index 87% rename from packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts rename to packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-record-field.ts index 51c3b77062..f61caa63dd 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-record-field.ts @@ -8,19 +8,23 @@ import { } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; import { camelToTitleCase } from 'src/utils/camel-to-title-case'; -export const generateFakeField = ({ +type GenerateFakeRecordFieldArgs = { + type: FieldMetadataType; + label: string; + fieldMetadataId: string; + icon?: string; + value?: string; +}; + +export const generateFakeRecordField = ({ type, label, icon, value, fieldMetadataId, -}: { - type: FieldMetadataType; - label: string; - fieldMetadataId?: string; - icon?: string; - value?: string; -}): (Leaf | Node) & { fieldMetadataId?: string } => { +}: GenerateFakeRecordFieldArgs): (Leaf | Node) & { + fieldMetadataId: string; +} => { const compositeType = compositeTypeDefinitions.get(type); if (compositeType) { diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts index 7034b4ab20..69c9959d43 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/utils/generate-object-record-fields.ts @@ -3,8 +3,8 @@ import { isDefined } from 'twenty-shared/utils'; import { type ObjectMetadataInfo } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; import { type FieldOutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type'; -import { generateFakeField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-field'; import { generateFakeObjectRecord } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-object-record'; +import { generateFakeRecordField } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/generate-fake-record-field'; import { shouldGenerateFieldFakeValue } from 'src/modules/workflow/workflow-builder/workflow-schema/utils/should-generate-field-fake-value'; const MAXIMUM_DEPTH = 1; @@ -25,7 +25,7 @@ export const generateObjectRecordFields = ({ } if (field.type !== FieldMetadataType.RELATION) { - acc[field.name] = generateFakeField({ + acc[field.name] = generateFakeRecordField({ type: field.type, label: field.label, icon: field.icon ?? undefined, diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts index ab6f9a9561..74103d9455 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service.ts @@ -73,7 +73,7 @@ export class WorkflowSchemaWorkspaceService { }); case WorkflowActionType.FORM: return this.computeFormActionOutputSchema({ - formMetadata: step.settings.input, + formFieldMetadataItems: step.settings.input, workspaceId, }); case WorkflowActionType.CODE: // StepOutput schema is computed on serverlessFunction draft execution @@ -156,10 +156,10 @@ export class WorkflowSchemaWorkspaceService { } private async computeFormActionOutputSchema({ - formMetadata, + formFieldMetadataItems, workspaceId, }: { - formMetadata: FormFieldMetadata[]; + formFieldMetadataItems: FormFieldMetadata[]; workspaceId: string; }): Promise { const objectMetadataMaps = @@ -168,7 +168,7 @@ export class WorkflowSchemaWorkspaceService { ); return generateFakeFormResponse({ - formMetadata, + formFieldMetadataItems, objectMetadataMaps, }); }