diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 8eae609b2..cc2d6ce5e 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -703,6 +703,12 @@ export type GetAuthorizationUrlForSsoOutput = { type: Scalars['String']['output']; }; +export type GetLoginTokenFromEmailVerificationTokenOutput = { + __typename?: 'GetLoginTokenFromEmailVerificationTokenOutput'; + loginToken: AuthToken; + workspaceUrls: WorkspaceUrls; +}; + export type GetServerlessFunctionSourceCodeInput = { /** The id of the function. */ id: Scalars['ID']['input']; @@ -908,7 +914,7 @@ export type Mutation = { getAuthTokensFromLoginToken: AuthTokens; getAuthorizationUrlForSSO: GetAuthorizationUrlForSsoOutput; getLoginTokenFromCredentials: LoginToken; - getLoginTokenFromEmailVerificationToken: LoginToken; + getLoginTokenFromEmailVerificationToken: GetLoginTokenFromEmailVerificationTokenOutput; impersonate: ImpersonateOutput; publishServerlessFunction: ServerlessFunction; renewToken: AuthTokens; @@ -2501,18 +2507,18 @@ export type WorkspaceNameAndId = { id: Scalars['String']['output']; }; +export type WorkspaceUrls = { + __typename?: 'WorkspaceUrls'; + customUrl?: Maybe; + subdomainUrl: Scalars['String']['output']; +}; + export type WorkspaceUrlsAndId = { __typename?: 'WorkspaceUrlsAndId'; id: Scalars['String']['output']; workspaceUrls: WorkspaceUrls; }; -export type WorkspaceUrls = { - __typename?: 'workspaceUrls'; - customUrl?: Maybe; - subdomainUrl: Scalars['String']['output']; -}; - export type RemoteServerFieldsFragment = { __typename?: 'RemoteServer', id: string, createdAt: any, foreignDataWrapperId: string, foreignDataWrapperOptions?: any | null, foreignDataWrapperType: string, updatedAt: any, schema?: string | null, label: string, userMappingOptions?: { __typename?: 'UserMappingOptionsUser', user?: string | null } | null }; export type RemoteTableFieldsFragment = { __typename?: 'RemoteTable', id?: any | null, name: string, schema?: string | null, status: RemoteTableStatus, schemaPendingUpdates?: Array | null }; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 7da66f05a..847f10098 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -627,6 +627,12 @@ export type GetAuthorizationUrlForSsoOutput = { type: Scalars['String']; }; +export type GetLoginTokenFromEmailVerificationTokenOutput = { + __typename?: 'GetLoginTokenFromEmailVerificationTokenOutput'; + loginToken: AuthToken; + workspaceUrls: WorkspaceUrls; +}; + export type GetServerlessFunctionSourceCodeInput = { /** The id of the function. */ id: Scalars['ID']; @@ -828,7 +834,7 @@ export type Mutation = { getAuthTokensFromLoginToken: AuthTokens; getAuthorizationUrlForSSO: GetAuthorizationUrlForSsoOutput; getLoginTokenFromCredentials: LoginToken; - getLoginTokenFromEmailVerificationToken: LoginToken; + getLoginTokenFromEmailVerificationToken: GetLoginTokenFromEmailVerificationTokenOutput; impersonate: ImpersonateOutput; publishServerlessFunction: ServerlessFunction; renewToken: AuthTokens; @@ -2278,18 +2284,18 @@ export type WorkspaceNameAndId = { id: Scalars['String']; }; +export type WorkspaceUrls = { + __typename?: 'WorkspaceUrls'; + customUrl?: Maybe; + subdomainUrl: Scalars['String']; +}; + export type WorkspaceUrlsAndId = { __typename?: 'WorkspaceUrlsAndId'; id: Scalars['String']; workspaceUrls: WorkspaceUrls; }; -export type WorkspaceUrls = { - __typename?: 'workspaceUrls'; - customUrl?: Maybe; - subdomainUrl: Scalars['String']; -}; - export type TimelineCalendarEventFragmentFragment = { __typename?: 'TimelineCalendarEvent', id: any, title: string, description: string, location: string, startsAt: string, endsAt: string, isFullDay: boolean, visibility: CalendarChannelVisibility, participants: Array<{ __typename?: 'TimelineCalendarEventParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }; export type TimelineCalendarEventParticipantFragmentFragment = { __typename?: 'TimelineCalendarEventParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }; @@ -2427,7 +2433,7 @@ export type GetLoginTokenFromEmailVerificationTokenMutationVariables = Exact<{ }>; -export type GetLoginTokenFromEmailVerificationTokenMutation = { __typename?: 'Mutation', getLoginTokenFromEmailVerificationToken: { __typename?: 'LoginToken', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } }; +export type GetLoginTokenFromEmailVerificationTokenMutation = { __typename?: 'Mutation', getLoginTokenFromEmailVerificationToken: { __typename?: 'GetLoginTokenFromEmailVerificationTokenOutput', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } }; export type ImpersonateMutationVariables = Exact<{ userId: Scalars['String']; @@ -2435,7 +2441,7 @@ export type ImpersonateMutationVariables = Exact<{ }>; -export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'ImpersonateOutput', workspace: { __typename?: 'WorkspaceUrlsAndId', id: string, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } }, loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } }; +export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'ImpersonateOutput', workspace: { __typename?: 'WorkspaceUrlsAndId', id: string, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } }, loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } }; export type RenewTokenMutationVariables = Exact<{ appToken: Scalars['String']; @@ -2462,12 +2468,12 @@ export type SignUpMutationVariables = Exact<{ }>; -export type SignUpMutation = { __typename?: 'Mutation', signUp: { __typename?: 'SignUpOutput', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, workspace: { __typename?: 'WorkspaceUrlsAndId', id: string, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } } }; +export type SignUpMutation = { __typename?: 'Mutation', signUp: { __typename?: 'SignUpOutput', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, workspace: { __typename?: 'WorkspaceUrlsAndId', id: string, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } } }; export type SignUpInNewWorkspaceMutationVariables = Exact<{ [key: string]: never; }>; -export type SignUpInNewWorkspaceMutation = { __typename?: 'Mutation', signUpInNewWorkspace: { __typename?: 'SignUpOutput', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, workspace: { __typename?: 'WorkspaceUrlsAndId', id: string, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } } }; +export type SignUpInNewWorkspaceMutation = { __typename?: 'Mutation', signUpInNewWorkspace: { __typename?: 'SignUpOutput', loginToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, workspace: { __typename?: 'WorkspaceUrlsAndId', id: string, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } } }; export type UpdatePasswordViaResetTokenMutationVariables = Exact<{ token: Scalars['String']; @@ -2483,12 +2489,12 @@ export type CheckUserExistsQueryVariables = Exact<{ }>; -export type CheckUserExistsQuery = { __typename?: 'Query', checkUserExists: { __typename: 'UserExists', exists: boolean, isEmailVerified: boolean, availableWorkspaces: Array<{ __typename?: 'AvailableWorkspaceOutput', id: string, displayName?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } | { __typename: 'UserNotExists', exists: boolean } }; +export type CheckUserExistsQuery = { __typename?: 'Query', checkUserExists: { __typename: 'UserExists', exists: boolean, isEmailVerified: boolean, availableWorkspaces: Array<{ __typename?: 'AvailableWorkspaceOutput', id: string, displayName?: string | null, logo?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, sso: Array<{ __typename?: 'SSOConnection', type: IdentityProviderType, id: string, issuer: string, name: string, status: SsoIdentityProviderStatus }> }> } | { __typename: 'UserNotExists', exists: boolean } }; export type GetPublicWorkspaceDataByDomainQueryVariables = Exact<{ [key: string]: never; }>; -export type GetPublicWorkspaceDataByDomainQuery = { __typename?: 'Query', getPublicWorkspaceDataByDomain: { __typename?: 'PublicWorkspaceDataOutput', id: string, logo?: string | null, displayName?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, authProviders: { __typename?: 'AuthProviders', google: boolean, magicLink: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> } } }; +export type GetPublicWorkspaceDataByDomainQuery = { __typename?: 'Query', getPublicWorkspaceDataByDomain: { __typename?: 'PublicWorkspaceDataOutput', id: string, logo?: string | null, displayName?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, authProviders: { __typename?: 'AuthProviders', google: boolean, magicLink: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> } } }; export type ValidatePasswordResetTokenQueryVariables = Exact<{ token: Scalars['String']; @@ -2681,7 +2687,7 @@ export type GetSsoIdentityProvidersQueryVariables = Exact<{ [key: string]: never export type GetSsoIdentityProvidersQuery = { __typename?: 'Query', getSSOIdentityProviders: Array<{ __typename?: 'FindAvailableSSOIDPOutput', type: IdentityProviderType, id: string, name: string, issuer: string, status: SsoIdentityProviderStatus }> }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> }; export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>; @@ -2698,7 +2704,7 @@ export type UploadProfilePictureMutation = { __typename?: 'Mutation', uploadProf export type GetCurrentUserQueryVariables = Exact<{ [key: string]: never; }>; -export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'workspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canAccessFullAdminPanel: boolean, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, userVars: any, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, workspaceMembers?: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, userEmail: string, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> | null, currentUserWorkspace?: { __typename?: 'UserWorkspace', settingsPermissions?: Array | null, objectRecordsPermissions?: Array | null } | null, currentWorkspace?: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: WorkspaceActivationStatus, isPublicInviteLinkEnabled: boolean, isGoogleAuthEnabled: boolean, isMicrosoftAuthEnabled: boolean, isPasswordAuthEnabled: boolean, subdomain: string, hasValidEnterpriseKey: boolean, customDomain?: string | null, isCustomDomainEnabled: boolean, metadataVersion: number, workspaceMembersCount?: number | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null }, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null, billingSubscriptions: Array<{ __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus }>, defaultRole?: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } | null } | null, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, subdomain: string, customDomain?: string | null, workspaceUrls: { __typename?: 'WorkspaceUrls', subdomainUrl: string, customUrl?: string | null } } | null }> } }; export type ActivateWorkflowVersionMutationVariables = Exact<{ workflowVersionId: Scalars['String']; @@ -3565,6 +3571,10 @@ export const GetLoginTokenFromEmailVerificationTokenDocument = gql` loginToken { ...AuthTokenFragment } + workspaceUrls { + subdomainUrl + customUrl + } } } ${AuthTokenFragmentFragmentDoc}`; diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 9c0a56f15..cbbb2ca44 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -109,14 +109,14 @@ const testCases: { { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: undefined }, { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined }, - { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: undefined }, - { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined }, + { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: AppPath.PlanRequired }, + { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' }, { loc: AppPath.VerifyEmail, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined }, - { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: undefined }, - { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: undefined }, - { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: undefined }, - { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: undefined }, - { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: undefined }, + { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WORKSPACE_ACTIVATION, res: AppPath.CreateWorkspace }, + { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PROFILE_CREATION, res: AppPath.CreateProfile }, + { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SYNC_EMAIL, res: AppPath.SyncEmails }, + { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.INVITE_TEAM, res: AppPath.InviteTeam }, + { loc: AppPath.VerifyEmail, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.COMPLETED, res: defaultHomePagePath }, { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PLAN_REQUIRED, res: AppPath.PlanRequired }, { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.COMPLETED, res: '/settings/billing' }, diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index 673261c81..a17c56ea6 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -19,16 +19,17 @@ export const usePageChangeEffectNavigateLocation = () => { const isWorkspaceSuspended = useIsWorkspaceActivationStatusEqualsTo( WorkspaceActivationStatus.SUSPENDED, ); + const { defaultHomePagePath } = useDefaultHomePagePath(); const isMatchingOpenRoute = isMatchingLocation(AppPath.Invite) || - isMatchingLocation(AppPath.ResetPassword) || - isMatchingLocation(AppPath.VerifyEmail); + isMatchingLocation(AppPath.ResetPassword); const isMatchingOngoingUserCreationRoute = isMatchingOpenRoute || isMatchingLocation(AppPath.SignInUp) || + isMatchingLocation(AppPath.VerifyEmail) || isMatchingLocation(AppPath.Verify); const isMatchingOnboardingRoute = diff --git a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx index aa40c846a..991b552a6 100644 --- a/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx +++ b/packages/twenty-front/src/modules/auth/components/VerifyEmailEffect.tsx @@ -3,12 +3,14 @@ import { AppPath } from '@/types/AppPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; -import { useReadCaptchaToken } from '@/captcha/hooks/useReadCaptchaToken'; import { useLingui } from '@lingui/react/macro'; import { useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useNavigateApp } from '~/hooks/useNavigateApp'; import { EmailVerificationSent } from '../sign-in-up/components/EmailVerificationSent'; +import { useRedirectToWorkspaceDomain } from '@/domain-manager/hooks/useRedirectToWorkspaceDomain'; +import { useVerifyLogin } from '@/auth/hooks/useVerifyLogin'; +import { getWorkspaceUrl } from '~/utils/getWorkspaceUrl'; export const VerifyEmailEffect = () => { const { getLoginTokenFromEmailVerificationToken } = useAuth(); @@ -21,7 +23,9 @@ export const VerifyEmailEffect = () => { const emailVerificationToken = searchParams.get('emailVerificationToken'); const navigate = useNavigateApp(); - const { readCaptchaToken } = useReadCaptchaToken(); + const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain(); + const { verifyLoginToken } = useVerifyLogin(); + const { t } = useLingui(); useEffect(() => { const verifyEmailToken = async () => { @@ -33,23 +37,25 @@ export const VerifyEmailEffect = () => { return navigate(AppPath.SignInUp); } - const captchaToken = await readCaptchaToken(); - try { - const { loginToken } = await getLoginTokenFromEmailVerificationToken( - emailVerificationToken, - captchaToken, - ); + const { loginToken, workspaceUrls } = + await getLoginTokenFromEmailVerificationToken(emailVerificationToken); enqueueSnackBar(t`Email verified.`, { dedupeKey: 'email-verification-dedupe-key', variant: SnackBarVariant.Success, }); - navigate(AppPath.Verify, undefined, { loginToken: loginToken.token }); + const workspaceUrl = getWorkspaceUrl(workspaceUrls); + if (workspaceUrl.slice(0, -1) !== window.location.origin) { + return redirectToWorkspaceDomain(workspaceUrl, AppPath.Verify, { + loginToken: loginToken.token, + }); + } + verifyLoginToken(loginToken.token); } catch (error) { enqueueSnackBar(t`Email verification failed.`, { - dedupeKey: 'email-verification-dedupe-key', + dedupeKey: 'email-verification-error-dedupe-key', variant: SnackBarVariant.Error, }); setIsError(true); diff --git a/packages/twenty-front/src/modules/auth/graphql/mutations/getLoginTokenFromEmailVerificationToken.ts b/packages/twenty-front/src/modules/auth/graphql/mutations/getLoginTokenFromEmailVerificationToken.ts index 67631c89e..028493638 100644 --- a/packages/twenty-front/src/modules/auth/graphql/mutations/getLoginTokenFromEmailVerificationToken.ts +++ b/packages/twenty-front/src/modules/auth/graphql/mutations/getLoginTokenFromEmailVerificationToken.ts @@ -12,6 +12,10 @@ export const GET_LOGIN_TOKEN_FROM_EMAIL_VERIFICATION_TOKEN = gql` loginToken { ...AuthTokenFragment } + workspaceUrls { + subdomainUrl + customUrl + } } } `; diff --git a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts index 526219a42..0b5e9e15c 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useAuth.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useAuth.ts @@ -13,7 +13,6 @@ import { iconsState } from 'twenty-ui'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState'; -import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState'; import { workspacesState } from '@/auth/states/workspaces'; import { billingState } from '@/client-config/states/billingState'; import { clientConfigApiStatusState } from '@/client-config/states/clientConfigApiStatusState'; @@ -87,7 +86,6 @@ export const useAuth = () => { const setSignInUpStep = useSetRecoilState(signInUpStepState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); - const setIsVerifyPendingState = useSetRecoilState(isVerifyPendingState); const setWorkspaces = useSetRecoilState(workspacesState); const { redirect } = useRedirect(); const { redirectToWorkspaceDomain } = useRedirectToWorkspaceDomain(); @@ -337,8 +335,6 @@ export const useAuth = () => { const handleGetAuthTokensFromLoginToken = useCallback( async (loginToken: string) => { - setIsVerifyPendingState(true); - const getAuthTokensResult = await getAuthTokensFromLoginToken({ variables: { loginToken }, }); @@ -356,15 +352,8 @@ export const useAuth = () => { ); await loadCurrentUser(); - - setIsVerifyPendingState(false); }, - [ - setIsVerifyPendingState, - getAuthTokensFromLoginToken, - setTokenPair, - loadCurrentUser, - ], + [getAuthTokensFromLoginToken, setTokenPair, loadCurrentUser], ); const handleCredentialsSignIn = useCallback( @@ -391,8 +380,6 @@ export const useAuth = () => { workspacePersonalInviteToken?: string, captchaToken?: string, ) => { - setIsVerifyPendingState(true); - const signUpResult = await signUp({ variables: { email, @@ -440,7 +427,6 @@ export const useAuth = () => { ); }, [ - setIsVerifyPendingState, signUp, workspacePublicData, isMultiWorkspaceEnabled, diff --git a/packages/twenty-front/src/modules/auth/hooks/useIsLogged.ts b/packages/twenty-front/src/modules/auth/hooks/useIsLogged.ts index 4399b564c..5f6165d06 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useIsLogged.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useIsLogged.ts @@ -1,12 +1,8 @@ -import { useRecoilState, useRecoilValue } from 'recoil'; - -import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState'; +import { useRecoilState } from 'recoil'; import { tokenPairState } from '../states/tokenPairState'; export const useIsLogged = (): boolean => { const [tokenPair] = useRecoilState(tokenPairState); - const isVerifyPending = useRecoilValue(isVerifyPendingState); - - return !!tokenPair && !isVerifyPending; + return !!tokenPair; }; diff --git a/packages/twenty-front/src/modules/auth/states/isVerifyPendingState.ts b/packages/twenty-front/src/modules/auth/states/isVerifyPendingState.ts deleted file mode 100644 index 0e554b0c8..000000000 --- a/packages/twenty-front/src/modules/auth/states/isVerifyPendingState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createState } from '@ui/utilities/state/utils/createState'; - -export const isVerifyPendingState = createState({ - key: 'isVerifyPendingState', - defaultValue: false, -}); diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts index 0a7548946..0bf51dbc3 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.resolver.ts @@ -52,6 +52,7 @@ import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { SettingPermissionType } from 'src/engine/metadata-modules/permissions/constants/setting-permission-type.constants'; import { PermissionsGraphqlApiExceptionFilter } from 'src/engine/metadata-modules/permissions/utils/permissions-graphql-api-exception.filter'; +import { GetLoginTokenFromEmailVerificationTokenOutput } from 'src/engine/core-modules/auth/dto/get-login-token-from-email-verification-token.output'; import { GetAuthTokensFromLoginTokenInput } from './dto/get-auth-tokens-from-login-token.input'; import { GetLoginTokenFromCredentialsInput } from './dto/get-login-token-from-credentials.input'; @@ -155,7 +156,7 @@ export class AuthResolver { return { loginToken }; } - @Mutation(() => LoginToken) + @Mutation(() => GetLoginTokenFromEmailVerificationTokenOutput) async getLoginTokenFromEmailVerificationToken( @Args() getLoginTokenFromEmailVerificationTokenInput: GetLoginTokenFromEmailVerificationTokenInput, @@ -179,7 +180,9 @@ export class AuthResolver { workspace.id, ); - return { loginToken }; + const workspaceUrls = this.domainManagerService.getWorkspaceUrls(workspace); + + return { loginToken, workspaceUrls }; } @UseGuards(CaptchaGuard) diff --git a/packages/twenty-server/src/engine/core-modules/auth/dto/available-workspaces.output.ts b/packages/twenty-server/src/engine/core-modules/auth/dto/available-workspaces.output.ts index bd849358b..361955467 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/dto/available-workspaces.output.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/dto/available-workspaces.output.ts @@ -7,7 +7,7 @@ import { IdentityProviderType, SSOIdentityProviderStatus, } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity'; -import { workspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; +import { WorkspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; @ObjectType() class SSOConnection { @@ -35,8 +35,8 @@ export class AvailableWorkspaceOutput { @Field(() => String, { nullable: true }) displayName?: string; - @Field(() => workspaceUrls) - workspaceUrls: workspaceUrls; + @Field(() => WorkspaceUrls) + workspaceUrls: WorkspaceUrls; @Field(() => String, { nullable: true }) logo?: string; diff --git a/packages/twenty-server/src/engine/core-modules/auth/dto/get-login-token-from-email-verification-token.output.ts b/packages/twenty-server/src/engine/core-modules/auth/dto/get-login-token-from-email-verification-token.output.ts new file mode 100644 index 000000000..d597f4116 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/auth/dto/get-login-token-from-email-verification-token.output.ts @@ -0,0 +1,14 @@ +import { Field, ObjectType } from '@nestjs/graphql'; + +import { WorkspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; + +import { AuthToken } from './token.entity'; + +@ObjectType() +export class GetLoginTokenFromEmailVerificationTokenOutput { + @Field(() => AuthToken) + loginToken: AuthToken; + + @Field(() => WorkspaceUrls) + workspaceUrls: WorkspaceUrls; +} diff --git a/packages/twenty-server/src/engine/core-modules/workspace/dtos/public-workspace-data-output.ts b/packages/twenty-server/src/engine/core-modules/workspace/dtos/public-workspace-data-output.ts index bf987f4db..720420b9e 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/dtos/public-workspace-data-output.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/dtos/public-workspace-data-output.ts @@ -5,7 +5,7 @@ import { IdentityProviderType, SSOIdentityProviderStatus, } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity'; -import { workspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; +import { WorkspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; @ObjectType() export class SSOIdentityProvider { @@ -57,6 +57,6 @@ export class PublicWorkspaceDataOutput { @Field(() => String, { nullable: true }) displayName: Workspace['displayName']; - @Field(() => workspaceUrls) - workspaceUrls: workspaceUrls; + @Field(() => WorkspaceUrls) + workspaceUrls: WorkspaceUrls; } diff --git a/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-subdomain-id.dto.ts b/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-subdomain-id.dto.ts index 2bb70e590..18c7f84e7 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-subdomain-id.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-subdomain-id.dto.ts @@ -1,11 +1,11 @@ import { Field, ObjectType } from '@nestjs/graphql'; -import { workspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; +import { WorkspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; @ObjectType() export class WorkspaceUrlsAndId { - @Field(() => workspaceUrls) - workspaceUrls: workspaceUrls; + @Field(() => WorkspaceUrls) + workspaceUrls: WorkspaceUrls; @Field() id: string; diff --git a/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-urls.dto.ts b/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-urls.dto.ts index 714dd0230..8a93371c4 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-urls.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/dtos/workspace-urls.dto.ts @@ -1,7 +1,7 @@ import { ObjectType, Field } from '@nestjs/graphql'; @ObjectType() -export class workspaceUrls { +export class WorkspaceUrls { @Field(() => String, { nullable: true }) customUrl?: string; diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts index cc2a84ccd..ac6388fd9 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.resolver.ts @@ -35,7 +35,7 @@ import { PublicWorkspaceDataOutput, } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output'; import { UpdateWorkspaceInput } from 'src/engine/core-modules/workspace/dtos/update-workspace-input'; -import { workspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; +import { WorkspaceUrls } from 'src/engine/core-modules/workspace/dtos/workspace-urls.dto'; import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util'; import { workspaceGraphqlApiExceptionHandler } from 'src/engine/core-modules/workspace/utils/workspace-graphql-api-exception-handler.util'; import { workspaceValidator } from 'src/engine/core-modules/workspace/workspace.validate'; @@ -251,7 +251,7 @@ export class WorkspaceResolver { return isDefined(this.environmentService.get('ENTERPRISE_KEY')); } - @ResolveField(() => workspaceUrls) + @ResolveField(() => WorkspaceUrls) workspaceUrls(@Parent() workspace: Workspace) { return this.domainManagerService.getWorkspaceUrls(workspace); }