From b27ccb0cfeb351ac107e5c671bd7340e56b6ba7a Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 18 Jun 2024 19:22:47 +0200 Subject: [PATCH 01/47] Migrate onboardingStep for createProfile --- .../twenty-front/src/generated/graphql.tsx | 21 +-- .../__test__/useOnboardingStatus.test.ts | 142 +++--------------- .../modules/auth/hooks/useOnboardingStatus.ts | 3 - .../__test__/getOnboardingStatus.test.ts | 51 +------ .../modules/auth/utils/getOnboardingStatus.ts | 11 +- .../src/pages/onboarding/CreateProfile.tsx | 11 ++ .../onboarding/enums/onboarding-step.enum.ts | 1 + .../onboarding/onboarding.service.ts | 31 ++-- .../engine/core-modules/user/user.resolver.ts | 5 +- .../workspace-member.repository.ts | 44 +++++- 10 files changed, 109 insertions(+), 211 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index d153c73c0ad..de292712d7d 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -291,7 +291,9 @@ export type Mutation = { deleteCurrentWorkspace: Workspace; deleteOneObject: Object; deleteUser: User; + disablePostgresProxy: PostgresCredentials; emailPasswordResetLink: EmailPasswordResetLink; + enablePostgresProxy: PostgresCredentials; exchangeAuthorizationCode: ExchangeAuthCode; generateApiKeyToken: ApiKeyToken; generateJWT: AuthTokens; @@ -462,6 +464,7 @@ export type ObjectFieldsConnection = { /** Onboarding step */ export enum OnboardingStep { InviteTeam = 'INVITE_TEAM', + ProfileCreation = 'PROFILE_CREATION', SyncEmail = 'SYNC_EMAIL' } @@ -483,6 +486,14 @@ export type PageInfo = { startCursor?: Maybe; }; +export type PostgresCredentials = { + __typename?: 'PostgresCredentials'; + id: Scalars['UUID']; + password: Scalars['String']; + user: Scalars['String']; + workspaceId: Scalars['String']; +}; + export type ProductPriceEntity = { __typename?: 'ProductPriceEntity'; created: Scalars['Float']; @@ -506,6 +517,7 @@ export type Query = { currentUser: User; currentWorkspace: Workspace; findWorkspaceFromInviteHash: Workspace; + getPostgresCredentials?: Maybe; getProductPrices: ProductPricesEntity; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; @@ -1061,8 +1073,6 @@ export type GetTimelineThreadsFromPersonIdQueryVariables = Exact<{ export type GetTimelineThreadsFromPersonIdQuery = { __typename?: 'Query', getTimelineThreadsFromPersonId: { __typename?: 'TimelineThreadsWithTotal', totalNumberOfThreads: number, timelineThreads: Array<{ __typename?: 'TimelineThread', id: any, read: boolean, visibility: MessageChannelVisibility, lastMessageReceivedAt: string, lastMessageBody: string, subject: string, numberOfMessagesInThread: number, participantCount: number, firstParticipant: { __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }, lastTwoParticipants: Array<{ __typename?: 'TimelineThreadParticipant', personId?: any | null, workspaceMemberId?: any | null, firstName: string, lastName: string, displayName: string, avatarUrl: string, handle: string }> }> } }; -export type TimelineThreadFragment = { __typename?: 'TimelineThread', id: any, subject: string, lastMessageReceivedAt: string }; - export type TrackMutationVariables = Exact<{ type: Scalars['String']; data: Scalars['JSON']; @@ -1364,13 +1374,6 @@ export const TimelineThreadsWithTotalFragmentFragmentDoc = gql` } } ${TimelineThreadFragmentFragmentDoc}`; -export const TimelineThreadFragmentDoc = gql` - fragment timelineThread on TimelineThread { - id - subject - lastMessageReceivedAt -} - `; export const AuthTokenFragmentFragmentDoc = gql` fragment AuthTokenFragment on AuthToken { token diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index 13d8e23fdaf..b97e240b700 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -4,7 +4,6 @@ import { RecoilRoot, useSetRecoilState } from 'recoil'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { CurrentUser, currentUserState } from '@/auth/states/currentUserState'; -import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { CurrentWorkspace, currentWorkspaceState, @@ -52,9 +51,6 @@ const renderHooks = () => { const onboardingStatus = useOnboardingStatus(); const setBilling = useSetRecoilState(billingState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); - const setCurrentWorkspaceMember = useSetRecoilState( - currentWorkspaceMemberState, - ); const setCurrentUser = useSetRecoilState(currentUserState); const setTokenPair = useSetRecoilState(tokenPairState); const setVerifyPending = useSetRecoilState(isVerifyPendingState); @@ -64,7 +60,6 @@ const renderHooks = () => { setBilling, setCurrentUser, setCurrentWorkspace, - setCurrentWorkspaceMember, setTokenPair, setVerifyPending, }; @@ -85,13 +80,8 @@ describe('useOnboardingStatus', () => { it('should return "incomplete"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -101,7 +91,6 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'incomplete', }); - setCurrentWorkspaceMember(currentWorkspaceMember); }); expect(result.current.onboardingStatus).toBe('incomplete'); @@ -109,13 +98,8 @@ describe('useOnboardingStatus', () => { it('should return "canceled"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -125,13 +109,6 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'canceled', }); - setCurrentWorkspaceMember({ - ...currentWorkspaceMember, - name: { - firstName: 'John', - lastName: 'Doe', - }, - }); }); expect(result.current.onboardingStatus).toBe('canceled'); @@ -160,23 +137,20 @@ describe('useOnboardingStatus', () => { it('should return "ongoing_profile_creation"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); setBilling(billing); - setCurrentUser(currentUser); + setCurrentUser({ + ...currentUser, + onboardingStep: OnboardingStep.ProfileCreation, + }); setCurrentWorkspace({ ...currentWorkspace, subscriptionStatus: 'active', }); - setCurrentWorkspaceMember(currentWorkspaceMember); }); expect(result.current.onboardingStatus).toBe('ongoing_profile_creation'); @@ -184,13 +158,8 @@ describe('useOnboardingStatus', () => { it('should return "ongoing_sync_email"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -203,13 +172,6 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'active', }); - setCurrentWorkspaceMember({ - ...currentWorkspaceMember, - name: { - firstName: 'John', - lastName: 'Doe', - }, - }); }); expect(result.current.onboardingStatus).toBe('ongoing_sync_email'); @@ -217,13 +179,8 @@ describe('useOnboardingStatus', () => { it('should return "ongoing_invite_team"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -236,13 +193,6 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'active', }); - setCurrentWorkspaceMember({ - ...currentWorkspaceMember, - name: { - firstName: 'John', - lastName: 'Doe', - }, - }); }); expect(result.current.onboardingStatus).toBe('ongoing_invite_team'); @@ -250,13 +200,8 @@ describe('useOnboardingStatus', () => { it('should return "completed"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -266,13 +211,6 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'active', }); - setCurrentWorkspaceMember({ - ...currentWorkspaceMember, - name: { - firstName: 'John', - lastName: 'Doe', - }, - }); }); expect(result.current.onboardingStatus).toBe('completed'); @@ -280,13 +218,8 @@ describe('useOnboardingStatus', () => { it('should return "past_due"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -296,13 +229,6 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'past_due', }); - setCurrentWorkspaceMember({ - ...currentWorkspaceMember, - name: { - firstName: 'John', - lastName: 'Doe', - }, - }); }); expect(result.current.onboardingStatus).toBe('past_due'); @@ -310,13 +236,8 @@ describe('useOnboardingStatus', () => { it('should return "unpaid"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -326,13 +247,6 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'unpaid', }); - setCurrentWorkspaceMember({ - ...currentWorkspaceMember, - name: { - firstName: 'John', - lastName: 'Doe', - }, - }); }); expect(result.current.onboardingStatus).toBe('unpaid'); @@ -340,13 +254,8 @@ describe('useOnboardingStatus', () => { it('should return "completed_without_subscription"', async () => { const { result } = renderHooks(); - const { - setTokenPair, - setBilling, - setCurrentUser, - setCurrentWorkspace, - setCurrentWorkspaceMember, - } = result.current; + const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + result.current; act(() => { setTokenPair(tokenPair); @@ -357,13 +266,6 @@ describe('useOnboardingStatus', () => { subscriptionStatus: 'trialing', currentBillingSubscription: null, }); - setCurrentWorkspaceMember({ - ...currentWorkspaceMember, - name: { - firstName: 'John', - lastName: 'Doe', - }, - }); }); expect(result.current.onboardingStatus).toBe( diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts index b638b19da85..00646456363 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts @@ -1,7 +1,6 @@ import { useRecoilValue } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; -import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { billingState } from '@/client-config/states/billingState'; @@ -13,14 +12,12 @@ import { export const useOnboardingStatus = (): OnboardingStatus | undefined => { const billing = useRecoilValue(billingState); - const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const currentWorkspace = useRecoilValue(currentWorkspaceState); const currentUser = useRecoilValue(currentUserState); const isLoggedIn = useIsLogged(); return getOnboardingStatus({ isLoggedIn, - currentWorkspaceMember, currentWorkspace, currentUser, isBillingEnabled: billing?.isBillingEnabled || false, diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts index 813e9b38299..eaf0b1edfa9 100644 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts @@ -1,6 +1,5 @@ import { CurrentUser } from '@/auth/states/currentUserState'; import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState'; -import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; import { OnboardingStep } from '~/generated/graphql'; import { getOnboardingStatus } from '../getOnboardingStatus'; @@ -9,7 +8,6 @@ describe('getOnboardingStatus', () => { it('should return the correct status', () => { const ongoingUserCreation = getOnboardingStatus({ isLoggedIn: false, - currentWorkspaceMember: null, currentWorkspace: null, currentUser: null, isBillingEnabled: false, @@ -17,7 +15,6 @@ describe('getOnboardingStatus', () => { const ongoingWorkspaceActivation = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: null, currentWorkspace: { id: '1', activationStatus: 'inactive', @@ -30,29 +27,18 @@ describe('getOnboardingStatus', () => { const ongoingProfileCreation = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: { - id: '1', - name: {}, - } as WorkspaceMember, currentWorkspace: { id: '1', activationStatus: 'active', } as CurrentWorkspace, currentUser: { - onboardingStep: null, + onboardingStep: OnboardingStep.ProfileCreation, } as CurrentUser, isBillingEnabled: false, }); const ongoingSyncEmail = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: { - id: '1', - name: { - firstName: 'John', - lastName: 'Doe', - }, - } as WorkspaceMember, currentWorkspace: { id: '1', activationStatus: 'active', @@ -65,13 +51,6 @@ describe('getOnboardingStatus', () => { const ongoingInviteTeam = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: { - id: '1', - name: { - firstName: 'John', - lastName: 'Doe', - }, - } as WorkspaceMember, currentWorkspace: { id: '1', activationStatus: 'active', @@ -84,13 +63,6 @@ describe('getOnboardingStatus', () => { const completed = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: { - id: '1', - name: { - firstName: 'John', - lastName: 'Doe', - }, - } as WorkspaceMember, currentWorkspace: { id: '1', activationStatus: 'active', @@ -103,13 +75,6 @@ describe('getOnboardingStatus', () => { const incomplete = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: { - id: '1', - name: { - firstName: 'John', - lastName: 'Doe', - }, - } as WorkspaceMember, currentWorkspace: { id: '1', activationStatus: 'active', @@ -123,13 +88,6 @@ describe('getOnboardingStatus', () => { const incompleteButBillingDisabled = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: { - id: '1', - name: { - firstName: 'John', - lastName: 'Doe', - }, - } as WorkspaceMember, currentWorkspace: { id: '1', activationStatus: 'active', @@ -143,13 +101,6 @@ describe('getOnboardingStatus', () => { const canceled = getOnboardingStatus({ isLoggedIn: true, - currentWorkspaceMember: { - id: '1', - name: { - firstName: 'John', - lastName: 'Doe', - }, - } as WorkspaceMember, currentWorkspace: { id: '1', activationStatus: 'active', diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index 0dc25470522..f517f804988 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -1,6 +1,5 @@ import { CurrentUser } from '@/auth/states/currentUserState'; import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState'; -import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; import { OnboardingStep } from '~/generated/graphql'; export enum OnboardingStatus { @@ -19,16 +18,11 @@ export enum OnboardingStatus { export const getOnboardingStatus = ({ isLoggedIn, - currentWorkspaceMember, currentWorkspace, currentUser, isBillingEnabled, }: { isLoggedIn: boolean; - currentWorkspaceMember: Omit< - WorkspaceMember, - 'createdAt' | 'updatedAt' | 'userId' | 'userEmail' | '__typename' - > | null; currentWorkspace: CurrentWorkspace | null; currentUser: CurrentUser | null; isBillingEnabled: boolean; @@ -54,10 +48,7 @@ export const getOnboardingStatus = ({ return OnboardingStatus.OngoingWorkspaceActivation; } - if ( - !currentWorkspaceMember?.name.firstName || - !currentWorkspaceMember?.name.lastName - ) { + if (currentUser.onboardingStep === OnboardingStep.ProfileCreation) { return OnboardingStatus.OngoingProfileCreation; } diff --git a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx index fa588b34ee9..69b78240e36 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx @@ -10,6 +10,7 @@ import { z } from 'zod'; import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; +import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; @@ -22,6 +23,7 @@ import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; +import { OnboardingStep } from '~/generated/graphql'; const StyledContentContainer = styled.div` width: 100%; @@ -59,6 +61,7 @@ export const CreateProfile = () => { const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState( currentWorkspaceMemberState, ); + const [currentUser, setCurrentUser] = useRecoilState(currentUserState); const { updateOneRecord } = useUpdateOneRecord({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, @@ -111,6 +114,13 @@ export const CreateProfile = () => { colorScheme: 'System', }) as any, ); + setCurrentUser( + (current) => + ({ + ...current, + onboardingStep: OnboardingStep.SyncEmail, + }) as any, + ); } catch (error: any) { enqueueSnackBar(error?.message, { variant: SnackBarVariant.Error, @@ -119,6 +129,7 @@ export const CreateProfile = () => { }, [ currentWorkspaceMember?.id, + setCurrentUser, enqueueSnackBar, setCurrentWorkspaceMember, updateOneRecord, diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts index 55087ce5aad..98466ecc268 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts @@ -1,4 +1,5 @@ export enum OnboardingStep { + PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', INVITE_TEAM = 'INVITE_TEAM', } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 1709f3d789b..567f5d4a9ab 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -8,6 +8,8 @@ import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/use import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository'; +import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; +import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; enum OnboardingStepValues { SKIPPED = 'SKIPPED', @@ -30,19 +32,21 @@ export class OnboardingService { private readonly keyValuePairService: KeyValuePairService, @InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity) private readonly connectedAccountRepository: ConnectedAccountRepository, + @InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity) + private readonly workspaceMemberRepository: WorkspaceMemberRepository, ) {} - private async isSyncEmailOnboardingStep(user: User, workspace: Workspace) { + private async isSyncEmailOnboardingStep(user: User) { const syncEmailValue = await this.keyValuePairService.get({ userId: user.id, - workspaceId: workspace.id, + workspaceId: user.defaultWorkspaceId, key: OnboardingStepKeys.SYNC_EMAIL_ONBOARDING_STEP, }); const isSyncEmailSkipped = syncEmailValue === OnboardingStepValues.SKIPPED; const connectedAccounts = await this.connectedAccountRepository.getAllByUserId( user.id, - workspace.id, + user.defaultWorkspaceId, ); return !isSyncEmailSkipped && !connectedAccounts?.length; @@ -64,15 +68,24 @@ export class OnboardingService { ); } - async getOnboardingStep( - user: User, - workspace: Workspace, - ): Promise { - if (await this.isSyncEmailOnboardingStep(user, workspace)) { + async getOnboardingStep(user: User): Promise { + const workspaceMember = await this.workspaceMemberRepository.getById( + user.id, + user.defaultWorkspaceId, + ); + + if ( + workspaceMember && + (!workspaceMember.name.firstName || !workspaceMember.name.lastName) + ) { + return OnboardingStep.PROFILE_CREATION; + } + + if (await this.isSyncEmailOnboardingStep(user)) { return OnboardingStep.SYNC_EMAIL; } - if (await this.isInviteTeamOnboardingStep(workspace)) { + if (await this.isInviteTeamOnboardingStep(user.defaultWorkspace)) { return OnboardingStep.INVITE_TEAM; } diff --git a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts index ef596d19d0a..f8a2cf0af5c 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts @@ -122,9 +122,6 @@ export class UserResolver { return null; } - return this.onboardingService.getOnboardingStep( - user, - user.defaultWorkspace, - ); + return this.onboardingService.getOnboardingStep(user); } } diff --git a/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts b/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts index c67c5e66fce..02daa7b67de 100644 --- a/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts +++ b/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts @@ -5,6 +5,8 @@ import { EntityManager } from 'typeorm'; import { WorkspaceDataSourceService } from 'src/engine/workspace-datasource/workspace-datasource.service'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { ObjectRecord } from 'src/engine/workspace-manager/workspace-sync-metadata/types/object-record'; +import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto'; +import { assert } from 'src/utils/assert'; @Injectable() export class WorkspaceMemberRepository { @@ -42,10 +44,14 @@ export class WorkspaceMemberRepository { return workspaceMembers?.[0]; } - public async getByIdOrFail( - userId: string, - workspaceId: string, - ): Promise> { + public async getById(userId: string, workspaceId: string) { + const schemaExists = + await this.workspaceDataSourceService.checkSchemaExists(workspaceId); + + if (!schemaExists) { + return; + } + const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(workspaceId); @@ -56,13 +62,39 @@ export class WorkspaceMemberRepository { workspaceId, ); - if (!workspaceMembers || workspaceMembers.length === 0) { + if (!workspaceMembers.length) { + return; + } + + assert( + workspaceMembers.length === 1, + 'WorkspaceMember not found or too many found', + ); + + const workspaceMember = new WorkspaceMember(); + + workspaceMember.id = workspaceMembers[0].id; + workspaceMember.colorScheme = workspaceMembers[0].colorScheme; + workspaceMember.locale = workspaceMembers[0].locale; + workspaceMember.avatarUrl = workspaceMembers[0].avatarUrl; + workspaceMember.name = { + firstName: workspaceMembers[0].nameFirstName, + lastName: workspaceMembers[0].nameLastName, + }; + + return workspaceMember; + } + + public async getByIdOrFail(userId: string, workspaceId: string) { + const workspaceMember = await this.getById(userId, workspaceId); + + if (!workspaceMember) { throw new NotFoundException( `No workspace member found for user ${userId} in workspace ${workspaceId}`, ); } - return workspaceMembers[0]; + return workspaceMember; } public async getAllByWorkspaceId( From 4a73025cc048331945e5ecfe638b5ddc79099cab Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 10:14:22 +0200 Subject: [PATCH 02/47] Migrate onboardingStep for activateWorkspace --- packages/twenty-front/src/generated/graphql.tsx | 3 ++- .../hooks/__test__/useOnboardingStatus.test.ts | 17 ++++------------- .../utils/__test__/getOnboardingStatus.test.ts | 2 +- .../modules/auth/utils/getOnboardingStatus.ts | 2 +- .../onboarding/enums/onboarding-step.enum.ts | 1 + .../onboarding/onboarding.module.ts | 8 +++++++- .../onboarding/onboarding.service.ts | 10 ++++++++++ 7 files changed, 26 insertions(+), 17 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index de292712d7d..83a4943ba55 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -465,7 +465,8 @@ export type ObjectFieldsConnection = { export enum OnboardingStep { InviteTeam = 'INVITE_TEAM', ProfileCreation = 'PROFILE_CREATION', - SyncEmail = 'SYNC_EMAIL' + SyncEmail = 'SYNC_EMAIL', + WorkspaceActivation = 'WORKSPACE_ACTIVATION' } export type OnboardingStepSuccess = { diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index b97e240b700..fc5f879923f 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -36,14 +36,6 @@ const currentWorkspace = { status: 'trialing', }, } as CurrentWorkspace; -const currentWorkspaceMember = { - id: '1', - locale: '', - name: { - firstName: '', - lastName: '', - }, -}; const renderHooks = () => { const { result } = renderHook( @@ -122,12 +114,11 @@ describe('useOnboardingStatus', () => { act(() => { setTokenPair(tokenPair); setBilling(billing); - setCurrentUser(currentUser); - setCurrentWorkspace({ - ...currentWorkspace, - activationStatus: 'inactive', - subscriptionStatus: 'active', + setCurrentUser({ + ...currentUser, + onboardingStep: OnboardingStep.WorkspaceActivation, }); + setCurrentWorkspace(currentWorkspace); }); expect(result.current.onboardingStatus).toBe( diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts index eaf0b1edfa9..d13e254f407 100644 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts @@ -20,7 +20,7 @@ describe('getOnboardingStatus', () => { activationStatus: 'inactive', } as CurrentWorkspace, currentUser: { - onboardingStep: null, + onboardingStep: OnboardingStep.WorkspaceActivation, } as CurrentUser, isBillingEnabled: false, }); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index f517f804988..bc10ca391a4 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -44,7 +44,7 @@ export const getOnboardingStatus = ({ return OnboardingStatus.Incomplete; } - if (currentWorkspace.activationStatus !== 'active') { + if (currentUser.onboardingStep === OnboardingStep.WorkspaceActivation) { return OnboardingStatus.OngoingWorkspaceActivation; } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts index 98466ecc268..ead4adf679f 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts @@ -1,4 +1,5 @@ export enum OnboardingStep { + WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', INVITE_TEAM = 'INVITE_TEAM', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts index 65f0156d3dd..654a19842d7 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts @@ -5,9 +5,15 @@ import { OnboardingResolver } from 'src/engine/core-modules/onboarding/onboardin import { KeyValuePairModule } from 'src/engine/core-modules/key-value-pair/key-value-pair.module'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; +import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module'; @Module({ - imports: [DataSourceModule, UserWorkspaceModule, KeyValuePairModule], + imports: [ + DataSourceModule, + WorkspaceManagerModule, + UserWorkspaceModule, + KeyValuePairModule, + ], exports: [OnboardingService], providers: [OnboardingService, OnboardingResolver], }) diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 567f5d4a9ab..19a3e52dd7e 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -10,6 +10,7 @@ import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/s import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; +import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service'; enum OnboardingStepValues { SKIPPED = 'SKIPPED', @@ -28,6 +29,7 @@ type OnboardingKeyValueType = { @Injectable() export class OnboardingService { constructor( + private readonly workspaceManagerService: WorkspaceManagerService, private readonly userWorkspaceService: UserWorkspaceService, private readonly keyValuePairService: KeyValuePairService, @InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity) @@ -69,6 +71,14 @@ export class OnboardingService { } async getOnboardingStep(user: User): Promise { + if ( + !(await this.workspaceManagerService.doesDataSourceExist( + user.defaultWorkspaceId, + )) + ) { + return OnboardingStep.WORKSPACE_ACTIVATION; + } + const workspaceMember = await this.workspaceMemberRepository.getById( user.id, user.defaultWorkspaceId, From 35e912f9c29e42f4d80b0b5870f285351d3f522f Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 10:25:55 +0200 Subject: [PATCH 03/47] Migrate onboardingStep for incomplete --- packages/twenty-front/src/generated/graphql.tsx | 1 + .../auth/hooks/__test__/useOnboardingStatus.test.ts | 8 ++++---- .../auth/utils/__test__/getOnboardingStatus.test.ts | 2 +- .../src/modules/auth/utils/getOnboardingStatus.ts | 5 +---- .../onboarding/enums/onboarding-step.enum.ts | 1 + .../engine/core-modules/onboarding/onboarding.module.ts | 2 ++ .../engine/core-modules/onboarding/onboarding.service.ts | 9 +++++++++ 7 files changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 83a4943ba55..d61800ee0b8 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -465,6 +465,7 @@ export type ObjectFieldsConnection = { export enum OnboardingStep { InviteTeam = 'INVITE_TEAM', ProfileCreation = 'PROFILE_CREATION', + SubscriptionIncomplete = 'SUBSCRIPTION_INCOMPLETE', SyncEmail = 'SYNC_EMAIL', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index fc5f879923f..53f67023978 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -78,11 +78,11 @@ describe('useOnboardingStatus', () => { act(() => { setTokenPair(tokenPair); setBilling(billing); - setCurrentUser(currentUser); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'incomplete', + setCurrentUser({ + ...currentUser, + onboardingStep: OnboardingStep.SubscriptionIncomplete, }); + setCurrentWorkspace(currentWorkspace); }); expect(result.current.onboardingStatus).toBe('incomplete'); diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts index d13e254f407..9034526fb78 100644 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts @@ -81,7 +81,7 @@ describe('getOnboardingStatus', () => { subscriptionStatus: 'incomplete', } as CurrentWorkspace, currentUser: { - onboardingStep: null, + onboardingStep: OnboardingStep.SubscriptionIncomplete, } as CurrentUser, isBillingEnabled: true, }); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index bc10ca391a4..2f17a7ed60c 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -37,10 +37,7 @@ export const getOnboardingStatus = ({ return undefined; } - if ( - isBillingEnabled && - currentWorkspace.subscriptionStatus === 'incomplete' - ) { + if (currentUser.onboardingStep === OnboardingStep.SubscriptionIncomplete) { return OnboardingStatus.Incomplete; } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts index ead4adf679f..9bfa9f7241f 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts @@ -1,4 +1,5 @@ export enum OnboardingStep { + SUBSCRIPTION_INCOMPLETE = 'SUBSCRIPTION_INCOMPLETE', WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts index 654a19842d7..8b9890850d0 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts @@ -6,6 +6,7 @@ import { KeyValuePairModule } from 'src/engine/core-modules/key-value-pair/key-v import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module'; +import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module'; @Module({ imports: [ @@ -13,6 +14,7 @@ import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-m WorkspaceManagerModule, UserWorkspaceModule, KeyValuePairModule, + EnvironmentModule, ], exports: [OnboardingService], providers: [OnboardingService, OnboardingResolver], diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 19a3e52dd7e..30f56646a66 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -11,6 +11,7 @@ import { ConnectedAccountRepository } from 'src/modules/connected-account/reposi import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service'; +import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; enum OnboardingStepValues { SKIPPED = 'SKIPPED', @@ -30,6 +31,7 @@ type OnboardingKeyValueType = { export class OnboardingService { constructor( private readonly workspaceManagerService: WorkspaceManagerService, + private readonly environmentService: EnvironmentService, private readonly userWorkspaceService: UserWorkspaceService, private readonly keyValuePairService: KeyValuePairService, @InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity) @@ -71,6 +73,13 @@ export class OnboardingService { } async getOnboardingStep(user: User): Promise { + if ( + this.environmentService.get('IS_BILLING_ENABLED') && + user.defaultWorkspace.subscriptionStatus === 'incomplete' + ) { + return OnboardingStep.SUBSCRIPTION_INCOMPLETE; + } + if ( !(await this.workspaceManagerService.doesDataSourceExist( user.defaultWorkspaceId, From 43f079f7c5d00a1ed9e0cd31fc77cf9625570803 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 10:38:35 +0200 Subject: [PATCH 04/47] Migrate onboardingStep for canceled, unpaid and past_due --- .../twenty-front/src/generated/graphql.tsx | 3 ++ .../__test__/useOnboardingStatus.test.ts | 24 +++++++-------- .../__test__/getOnboardingStatus.test.ts | 30 ++++++++++++++++++- .../modules/auth/utils/getOnboardingStatus.ts | 6 ++-- .../onboarding/enums/onboarding-step.enum.ts | 3 ++ .../onboarding/onboarding.service.ts | 23 ++++++++++---- 6 files changed, 68 insertions(+), 21 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index d61800ee0b8..4b0ae39fc43 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -465,7 +465,10 @@ export type ObjectFieldsConnection = { export enum OnboardingStep { InviteTeam = 'INVITE_TEAM', ProfileCreation = 'PROFILE_CREATION', + SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', SubscriptionIncomplete = 'SUBSCRIPTION_INCOMPLETE', + SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', + SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index 53f67023978..421bbc00616 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -96,11 +96,11 @@ describe('useOnboardingStatus', () => { act(() => { setTokenPair(tokenPair); setBilling(billing); - setCurrentUser(currentUser); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'canceled', + setCurrentUser({ + ...currentUser, + onboardingStep: OnboardingStep.SubscriptionCanceled, }); + setCurrentWorkspace(currentWorkspace); }); expect(result.current.onboardingStatus).toBe('canceled'); @@ -215,11 +215,11 @@ describe('useOnboardingStatus', () => { act(() => { setTokenPair(tokenPair); setBilling(billing); - setCurrentUser(currentUser); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'past_due', + setCurrentUser({ + ...currentUser, + onboardingStep: OnboardingStep.SubscriptionPastDue, }); + setCurrentWorkspace(currentWorkspace); }); expect(result.current.onboardingStatus).toBe('past_due'); @@ -233,11 +233,11 @@ describe('useOnboardingStatus', () => { act(() => { setTokenPair(tokenPair); setBilling(billing); - setCurrentUser(currentUser); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'unpaid', + setCurrentUser({ + ...currentUser, + onboardingStep: OnboardingStep.SubscriptionUnpaid, }); + setCurrentWorkspace(currentWorkspace); }); expect(result.current.onboardingStatus).toBe('unpaid'); diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts index 9034526fb78..bad69cd192e 100644 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts @@ -107,7 +107,33 @@ describe('getOnboardingStatus', () => { subscriptionStatus: 'canceled', } as CurrentWorkspace, currentUser: { - onboardingStep: null, + onboardingStep: OnboardingStep.SubscriptionCanceled, + } as CurrentUser, + isBillingEnabled: true, + }); + + const past_due = getOnboardingStatus({ + isLoggedIn: true, + currentWorkspace: { + id: '1', + activationStatus: 'active', + subscriptionStatus: 'past_due', + } as CurrentWorkspace, + currentUser: { + onboardingStep: OnboardingStep.SubscriptionPastDue, + } as CurrentUser, + isBillingEnabled: true, + }); + + const unpaid = getOnboardingStatus({ + isLoggedIn: true, + currentWorkspace: { + id: '1', + activationStatus: 'active', + subscriptionStatus: 'unpaid', + } as CurrentWorkspace, + currentUser: { + onboardingStep: OnboardingStep.SubscriptionUnpaid, } as CurrentUser, isBillingEnabled: true, }); @@ -120,6 +146,8 @@ describe('getOnboardingStatus', () => { expect(completed).toBe('completed'); expect(incomplete).toBe('incomplete'); expect(canceled).toBe('canceled'); + expect(past_due).toBe('past_due'); + expect(unpaid).toBe('unpaid'); expect(incompleteButBillingDisabled).toBe('completed'); }); }); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index 2f17a7ed60c..a76f639d102 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -57,15 +57,15 @@ export const getOnboardingStatus = ({ return OnboardingStatus.OngoingInviteTeam; } - if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'canceled') { + if (currentUser.onboardingStep === OnboardingStep.SubscriptionCanceled) { return OnboardingStatus.Canceled; } - if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'past_due') { + if (currentUser.onboardingStep === OnboardingStep.SubscriptionPastDue) { return OnboardingStatus.PastDue; } - if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'unpaid') { + if (currentUser.onboardingStep === OnboardingStep.SubscriptionUnpaid) { return OnboardingStatus.Unpaid; } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts index 9bfa9f7241f..f4bad4c86ae 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts @@ -1,5 +1,8 @@ export enum OnboardingStep { SUBSCRIPTION_INCOMPLETE = 'SUBSCRIPTION_INCOMPLETE', + SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED', + SUBSCRIPTION_PAST_DUE = 'SUBSCRIPTION_PAST_DUE', + SUBSCRIPTION_UNPAID = 'SUBSCRIPTION_UNPAID', WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 30f56646a66..b5e742dec0b 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -73,11 +73,10 @@ export class OnboardingService { } async getOnboardingStep(user: User): Promise { - if ( - this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === 'incomplete' - ) { - return OnboardingStep.SUBSCRIPTION_INCOMPLETE; + if (this.environmentService.get('IS_BILLING_ENABLED')) { + if (user.defaultWorkspace.subscriptionStatus === 'incomplete') { + return OnboardingStep.SUBSCRIPTION_INCOMPLETE; + } } if ( @@ -108,6 +107,20 @@ export class OnboardingService { return OnboardingStep.INVITE_TEAM; } + if (this.environmentService.get('IS_BILLING_ENABLED')) { + if (user.defaultWorkspace.subscriptionStatus === 'canceled') { + return OnboardingStep.SUBSCRIPTION_CANCELED; + } + + if (user.defaultWorkspace.subscriptionStatus === 'past_due') { + return OnboardingStep.SUBSCRIPTION_PAST_DUE; + } + + if (user.defaultWorkspace.subscriptionStatus === 'unpaid') { + return OnboardingStep.SUBSCRIPTION_UNPAID; + } + } + return null; } From 968dedeb81e523af3958b5bc421e8996d3f005a6 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 10:50:30 +0200 Subject: [PATCH 05/47] Migrate onboardingStep for completed without subscription --- packages/twenty-front/src/generated/graphql.tsx | 1 + .../hooks/__test__/useOnboardingStatus.test.ts | 9 ++++----- .../utils/__test__/getOnboardingStatus.test.ts | 14 ++++++++++++++ .../src/modules/auth/utils/getOnboardingStatus.ts | 4 +++- .../onboarding/enums/onboarding-step.enum.ts | 1 + .../core-modules/onboarding/onboarding.module.ts | 2 ++ .../core-modules/onboarding/onboarding.service.ts | 10 ++++++++++ 7 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 4b0ae39fc43..eb13cb1ed51 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -463,6 +463,7 @@ export type ObjectFieldsConnection = { /** Onboarding step */ export enum OnboardingStep { + CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', ProfileCreation = 'PROFILE_CREATION', SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index 421bbc00616..fe4b6b77a83 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -251,12 +251,11 @@ describe('useOnboardingStatus', () => { act(() => { setTokenPair(tokenPair); setBilling(billing); - setCurrentUser(currentUser); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'trialing', - currentBillingSubscription: null, + setCurrentUser({ + ...currentUser, + onboardingStep: OnboardingStep.CompletedWithoutSubscription, }); + setCurrentWorkspace(currentWorkspace); }); expect(result.current.onboardingStatus).toBe( diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts index bad69cd192e..2c152df4472 100644 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts @@ -138,6 +138,19 @@ describe('getOnboardingStatus', () => { isBillingEnabled: true, }); + const completeWithoutSubscription = getOnboardingStatus({ + isLoggedIn: true, + currentWorkspace: { + id: '1', + activationStatus: 'active', + subscriptionStatus: 'unpaid', + } as CurrentWorkspace, + currentUser: { + onboardingStep: OnboardingStep.CompletedWithoutSubscription, + } as CurrentUser, + isBillingEnabled: false, + }); + expect(ongoingUserCreation).toBe('ongoing_user_creation'); expect(ongoingWorkspaceActivation).toBe('ongoing_workspace_activation'); expect(ongoingProfileCreation).toBe('ongoing_profile_creation'); @@ -149,5 +162,6 @@ describe('getOnboardingStatus', () => { expect(past_due).toBe('past_due'); expect(unpaid).toBe('unpaid'); expect(incompleteButBillingDisabled).toBe('completed'); + expect(completeWithoutSubscription).toBe('completed_without_subscription'); }); }); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index a76f639d102..5e4007b1c9b 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -69,7 +69,9 @@ export const getOnboardingStatus = ({ return OnboardingStatus.Unpaid; } - if (isBillingEnabled && !currentWorkspace.currentBillingSubscription) { + if ( + currentUser.onboardingStep === OnboardingStep.CompletedWithoutSubscription + ) { return OnboardingStatus.CompletedWithoutSubscription; } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts index f4bad4c86ae..e1b9ebb6a66 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts @@ -7,4 +7,5 @@ export enum OnboardingStep { PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', INVITE_TEAM = 'INVITE_TEAM', + COMPLETED_WITHOUT_SUBSCRIPTION = 'COMPLETED_WITHOUT_SUBSCRIPTION', } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts index 8b9890850d0..a036cafee61 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts @@ -7,6 +7,7 @@ import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module'; import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module'; import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module'; +import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; @Module({ imports: [ @@ -15,6 +16,7 @@ import { EnvironmentModule } from 'src/engine/integrations/environment/environme UserWorkspaceModule, KeyValuePairModule, EnvironmentModule, + BillingModule, ], exports: [OnboardingService], providers: [OnboardingService, OnboardingResolver], diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index b5e742dec0b..cda2db19fd8 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -12,6 +12,7 @@ import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/sta import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; enum OnboardingStepValues { SKIPPED = 'SKIPPED', @@ -30,6 +31,7 @@ type OnboardingKeyValueType = { @Injectable() export class OnboardingService { constructor( + private readonly billingService: BillingService, private readonly workspaceManagerService: WorkspaceManagerService, private readonly environmentService: EnvironmentService, private readonly userWorkspaceService: UserWorkspaceService, @@ -119,6 +121,14 @@ export class OnboardingService { if (user.defaultWorkspace.subscriptionStatus === 'unpaid') { return OnboardingStep.SUBSCRIPTION_UNPAID; } + + if ( + !(await this.billingService.getCurrentBillingSubscription({ + workspaceId: user.defaultWorkspaceId, + })) + ) { + return OnboardingStep.COMPLETED_WITHOUT_SUBSCRIPTION; + } } return null; From b210b5036af4c40fd49c8e8f13a8d2023565b7d1 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 11:02:17 +0200 Subject: [PATCH 06/47] Migrate onboardingStep for completed --- packages/twenty-front/src/generated/graphql.tsx | 1 + .../core-modules/onboarding/enums/onboarding-step.enum.ts | 1 + .../src/engine/core-modules/onboarding/onboarding.service.ts | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index eb13cb1ed51..26705dd7282 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -463,6 +463,7 @@ export type ObjectFieldsConnection = { /** Onboarding step */ export enum OnboardingStep { + Completed = 'COMPLETED', CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', ProfileCreation = 'PROFILE_CREATION', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts index e1b9ebb6a66..20b72a8c33d 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts @@ -8,4 +8,5 @@ export enum OnboardingStep { SYNC_EMAIL = 'SYNC_EMAIL', INVITE_TEAM = 'INVITE_TEAM', COMPLETED_WITHOUT_SUBSCRIPTION = 'COMPLETED_WITHOUT_SUBSCRIPTION', + COMPLETED = 'COMPLETED', } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index cda2db19fd8..fb198f2bd4d 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -131,7 +131,7 @@ export class OnboardingService { } } - return null; + return OnboardingStep.COMPLETED; } async skipInviteTeamOnboardingStep(workspaceId: string) { From ecce5a67ce94b33523754db16bde308811ce0cb2 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 11:05:13 +0200 Subject: [PATCH 07/47] Remove useless parameter --- .../__test__/useOnboardingStatus.test.ts | 33 +++------- .../modules/auth/hooks/useOnboardingStatus.ts | 6 -- .../__test__/getOnboardingStatus.test.ts | 64 ------------------- .../modules/auth/utils/getOnboardingStatus.ts | 7 +- 4 files changed, 11 insertions(+), 99 deletions(-) diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index fe4b6b77a83..37000091791 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -10,7 +10,6 @@ import { } from '@/auth/states/currentWorkspaceState'; import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState'; import { tokenPairState } from '@/auth/states/tokenPairState'; -import { billingState } from '@/client-config/states/billingState'; import { OnboardingStep } from '~/generated/graphql'; const tokenPair = { @@ -41,7 +40,6 @@ const renderHooks = () => { const { result } = renderHook( () => { const onboardingStatus = useOnboardingStatus(); - const setBilling = useSetRecoilState(billingState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); const setCurrentUser = useSetRecoilState(currentUserState); const setTokenPair = useSetRecoilState(tokenPairState); @@ -49,7 +47,6 @@ const renderHooks = () => { return { onboardingStatus, - setBilling, setCurrentUser, setCurrentWorkspace, setTokenPair, @@ -72,12 +69,11 @@ describe('useOnboardingStatus', () => { it('should return "incomplete"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.SubscriptionIncomplete, @@ -90,12 +86,11 @@ describe('useOnboardingStatus', () => { it('should return "canceled"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.SubscriptionCanceled, @@ -108,12 +103,11 @@ describe('useOnboardingStatus', () => { it('should return "ongoing_workspace_activation"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.WorkspaceActivation, @@ -128,12 +122,11 @@ describe('useOnboardingStatus', () => { it('should return "ongoing_profile_creation"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.ProfileCreation, @@ -149,12 +142,11 @@ describe('useOnboardingStatus', () => { it('should return "ongoing_sync_email"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.SyncEmail, @@ -170,12 +162,11 @@ describe('useOnboardingStatus', () => { it('should return "ongoing_invite_team"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.InviteTeam, @@ -191,12 +182,11 @@ describe('useOnboardingStatus', () => { it('should return "completed"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser(currentUser); setCurrentWorkspace({ ...currentWorkspace, @@ -209,12 +199,11 @@ describe('useOnboardingStatus', () => { it('should return "past_due"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.SubscriptionPastDue, @@ -227,12 +216,11 @@ describe('useOnboardingStatus', () => { it('should return "unpaid"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.SubscriptionUnpaid, @@ -245,12 +233,11 @@ describe('useOnboardingStatus', () => { it('should return "completed_without_subscription"', async () => { const { result } = renderHooks(); - const { setTokenPair, setBilling, setCurrentUser, setCurrentWorkspace } = + const { setTokenPair, setCurrentUser, setCurrentWorkspace } = result.current; act(() => { setTokenPair(tokenPair); - setBilling(billing); setCurrentUser({ ...currentUser, onboardingStep: OnboardingStep.CompletedWithoutSubscription, diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts index 00646456363..7afcb4b9e65 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts @@ -1,8 +1,6 @@ import { useRecoilValue } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; -import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { billingState } from '@/client-config/states/billingState'; import { useIsLogged } from '../hooks/useIsLogged'; import { @@ -11,15 +9,11 @@ import { } from '../utils/getOnboardingStatus'; export const useOnboardingStatus = (): OnboardingStatus | undefined => { - const billing = useRecoilValue(billingState); - const currentWorkspace = useRecoilValue(currentWorkspaceState); const currentUser = useRecoilValue(currentUserState); const isLoggedIn = useIsLogged(); return getOnboardingStatus({ isLoggedIn, - currentWorkspace, currentUser, - isBillingEnabled: billing?.isBillingEnabled || false, }); }; diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts index 2c152df4472..836067fd273 100644 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts @@ -1,5 +1,4 @@ import { CurrentUser } from '@/auth/states/currentUserState'; -import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState'; import { OnboardingStep } from '~/generated/graphql'; import { getOnboardingStatus } from '../getOnboardingStatus'; @@ -8,147 +7,84 @@ describe('getOnboardingStatus', () => { it('should return the correct status', () => { const ongoingUserCreation = getOnboardingStatus({ isLoggedIn: false, - currentWorkspace: null, currentUser: null, - isBillingEnabled: false, }); const ongoingWorkspaceActivation = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'inactive', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.WorkspaceActivation, } as CurrentUser, - isBillingEnabled: false, }); const ongoingProfileCreation = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.ProfileCreation, } as CurrentUser, - isBillingEnabled: false, }); const ongoingSyncEmail = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.SyncEmail, } as CurrentUser, - isBillingEnabled: false, }); const ongoingInviteTeam = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.InviteTeam, } as CurrentUser, - isBillingEnabled: false, }); const completed = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - } as CurrentWorkspace, currentUser: { onboardingStep: null, } as CurrentUser, - isBillingEnabled: false, }); const incomplete = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - subscriptionStatus: 'incomplete', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.SubscriptionIncomplete, } as CurrentUser, - isBillingEnabled: true, }); const incompleteButBillingDisabled = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - subscriptionStatus: 'incomplete', - } as CurrentWorkspace, currentUser: { onboardingStep: null, } as CurrentUser, - isBillingEnabled: false, }); const canceled = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - subscriptionStatus: 'canceled', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.SubscriptionCanceled, } as CurrentUser, - isBillingEnabled: true, }); const past_due = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - subscriptionStatus: 'past_due', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.SubscriptionPastDue, } as CurrentUser, - isBillingEnabled: true, }); const unpaid = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - subscriptionStatus: 'unpaid', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.SubscriptionUnpaid, } as CurrentUser, - isBillingEnabled: true, }); const completeWithoutSubscription = getOnboardingStatus({ isLoggedIn: true, - currentWorkspace: { - id: '1', - activationStatus: 'active', - subscriptionStatus: 'unpaid', - } as CurrentWorkspace, currentUser: { onboardingStep: OnboardingStep.CompletedWithoutSubscription, } as CurrentUser, - isBillingEnabled: false, }); expect(ongoingUserCreation).toBe('ongoing_user_creation'); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index 5e4007b1c9b..b6f35ef4e2e 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -1,5 +1,4 @@ import { CurrentUser } from '@/auth/states/currentUserState'; -import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState'; import { OnboardingStep } from '~/generated/graphql'; export enum OnboardingStatus { @@ -18,14 +17,10 @@ export enum OnboardingStatus { export const getOnboardingStatus = ({ isLoggedIn, - currentWorkspace, currentUser, - isBillingEnabled, }: { isLoggedIn: boolean; - currentWorkspace: CurrentWorkspace | null; currentUser: CurrentUser | null; - isBillingEnabled: boolean; }) => { if (!isLoggedIn) { return OnboardingStatus.OngoingUserCreation; @@ -33,7 +28,7 @@ export const getOnboardingStatus = ({ // After SignInUp, the user should have a current workspace assigned. // If not, it indicates that the data is still being requested. - if (!currentWorkspace || !currentUser) { + if (!currentUser) { return undefined; } From b0ada5c5e2cdb0861e5c6ffc52f55569906411a9 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 11:13:54 +0200 Subject: [PATCH 08/47] Add userCreation onboardingStep --- .../twenty-front/src/generated/graphql.tsx | 1 + ...sePageChangeEffectNavigateLocation.test.ts | 463 ++++++++--------- .../usePageChangeEffectNavigateLocation.ts | 25 +- .../modules/auth/hooks/useOnboardingStatus.ts | 8 +- .../modules/auth/utils/getOnboardingStatus.ts | 42 +- .../hooks/useSetNextOnboardingStep.ts | 16 +- .../hooks/__tests__/useShowAuthModal.test.tsx | 467 +++++++++--------- .../ui/layout/hooks/useShowAuthModal.ts | 25 +- .../src/pages/onboarding/CreateProfile.tsx | 7 +- .../src/pages/onboarding/CreateWorkspace.tsx | 8 +- .../src/pages/settings/SettingsBilling.tsx | 14 +- .../onboarding/enums/onboarding-step.enum.ts | 1 + 12 files changed, 530 insertions(+), 547 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 26705dd7282..7b8b054f090 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -472,6 +472,7 @@ export enum OnboardingStep { SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', + UserCreation = 'USER_CREATION', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index cf9a7730cd9..ca8181e4312 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -1,6 +1,7 @@ import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { AppPath } from '@/types/AppPath'; +import { OnboardingStep } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation'; @@ -28,257 +29,257 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({ // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Verify, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.Verify, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.Verify, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingUserCreation, res: undefined }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Verify, status: OnboardingStep.UserCreation, res: undefined }, + { loc: AppPath.Verify, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Verify, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Verify, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Verify, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Verify, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.Verify, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.SignInUp, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingUserCreation, res: undefined }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SignInUp, status: OnboardingStep.UserCreation, res: undefined }, + { loc: AppPath.SignInUp, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SignInUp, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SignInUp, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SignInUp, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SignInUp, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.Invite, status: OnboardingStatus.Incomplete, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.Canceled, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.Unpaid, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingUserCreation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingWorkspaceActivation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingProfileCreation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingSyncEmail, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingInviteTeam, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionCanceled, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.UserCreation, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.WorkspaceActivation, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.ProfileCreation, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.SyncEmail, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.InviteTeam, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Incomplete, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Canceled, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Unpaid, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingUserCreation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingWorkspaceActivation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingProfileCreation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingSyncEmail, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingInviteTeam, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionCanceled, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.UserCreation, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.WorkspaceActivation, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.ProfileCreation, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SyncEmail, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.InviteTeam, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingWorkspaceActivation, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.WorkspaceActivation, res: undefined }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingProfileCreation, res: undefined }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.CreateProfile, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.CreateProfile, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.CreateProfile, status: OnboardingStep.ProfileCreation, res: undefined }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateProfile, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateProfile, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingSyncEmail, res: undefined }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SyncEmails, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.SyncEmails, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SyncEmails, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SyncEmail, res: undefined }, + { loc: AppPath.SyncEmails, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SyncEmails, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingInviteTeam, res: undefined }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.InviteTeam, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.InviteTeam, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.InviteTeam, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.InviteTeam, status: OnboardingStep.InviteTeam, res: undefined }, + { loc: AppPath.InviteTeam, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Incomplete, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Canceled, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Unpaid, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionCanceled, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequired, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.PlanRequired, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequired, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequired, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequired, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingWorkspaceActivation, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.WorkspaceActivation, res: undefined }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Index, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.Index, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.Index, status: OnboardingStatus.PastDue, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Index, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionPastDue, res: defaultHomePagePath }, + { loc: AppPath.Index, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Index, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Index, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Index, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Index, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Index, status: OnboardingStep.Completed, res: defaultHomePagePath }, + { loc: AppPath.Index, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.TasksPage, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.TasksPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.TasksPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.TasksPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.TasksPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.TasksPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.TasksPage, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.TasksPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Canceled, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Unpaid, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionCanceled, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.Impersonate, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Impersonate, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Impersonate, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Impersonate, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Impersonate, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Impersonate, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Impersonate, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.Impersonate, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Authorize, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.Authorize, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.Authorize, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Authorize, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Authorize, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Authorize, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Authorize, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Authorize, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Authorize, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.Authorize, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStatus.Incomplete, res: AppPath.PlanRequired }, - { loc: AppPath.NotFound, status: OnboardingStatus.Canceled, res: '/settings/billing' }, - { loc: AppPath.NotFound, status: OnboardingStatus.Unpaid, res: '/settings/billing' }, - { loc: AppPath.NotFound, status: OnboardingStatus.PastDue, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingUserCreation, res: AppPath.SignInUp }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingWorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingSyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingInviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFound, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionPastDue, res: undefined }, + { loc: AppPath.NotFound, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.NotFound, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFound, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFound, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFound, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFound, status: OnboardingStep.Completed, res: undefined }, + { loc: AppPath.NotFound, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, ]; describe('usePageChangeEffectNavigateLocation', () => { diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index 5b34f0771ed..e6c4aed2db2 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -1,7 +1,7 @@ import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; -import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; +import { OnboardingStep } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { isDefined } from '~/utils/isDefined'; @@ -34,14 +34,14 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStatus.OngoingUserCreation && + onboardingStatus === OnboardingStep.UserCreation && !isMatchingOngoingUserCreationRoute ) { return AppPath.SignInUp; } if ( - onboardingStatus === OnboardingStatus.Incomplete && + onboardingStatus === OnboardingStep.SubscriptionIncomplete && !isMatchingLocation(AppPath.PlanRequired) ) { return AppPath.PlanRequired; @@ -49,9 +49,10 @@ export const usePageChangeEffectNavigateLocation = () => { if ( isDefined(onboardingStatus) && - [OnboardingStatus.Unpaid, OnboardingStatus.Canceled].includes( - onboardingStatus, - ) && + [ + OnboardingStep.SubscriptionUnpaid, + OnboardingStep.SubscriptionCanceled, + ].includes(onboardingStatus) && !( isMatchingLocation(AppPath.SettingsCatchAll) || isMatchingLocation(AppPath.PlanRequired) @@ -63,7 +64,7 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStatus.OngoingWorkspaceActivation && + onboardingStatus === OnboardingStep.WorkspaceActivation && !isMatchingLocation(AppPath.CreateWorkspace) && !isMatchingLocation(AppPath.PlanRequiredSuccess) ) { @@ -71,35 +72,35 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStatus.OngoingProfileCreation && + onboardingStatus === OnboardingStep.ProfileCreation && !isMatchingLocation(AppPath.CreateProfile) ) { return AppPath.CreateProfile; } if ( - onboardingStatus === OnboardingStatus.OngoingSyncEmail && + onboardingStatus === OnboardingStep.SyncEmail && !isMatchingLocation(AppPath.SyncEmails) ) { return AppPath.SyncEmails; } if ( - onboardingStatus === OnboardingStatus.OngoingInviteTeam && + onboardingStatus === OnboardingStep.InviteTeam && !isMatchingLocation(AppPath.InviteTeam) ) { return AppPath.InviteTeam; } if ( - onboardingStatus === OnboardingStatus.Completed && + onboardingStatus === OnboardingStep.Completed && isMatchingOnboardingRoute ) { return defaultHomePagePath; } if ( - onboardingStatus === OnboardingStatus.CompletedWithoutSubscription && + onboardingStatus === OnboardingStep.CompletedWithoutSubscription && isMatchingOnboardingRoute && !isMatchingLocation(AppPath.PlanRequired) ) { diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts index 7afcb4b9e65..5c9d394f1cd 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts @@ -1,14 +1,12 @@ import { useRecoilValue } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; +import { OnboardingStep } from '~/generated/graphql'; import { useIsLogged } from '../hooks/useIsLogged'; -import { - getOnboardingStatus, - OnboardingStatus, -} from '../utils/getOnboardingStatus'; +import { getOnboardingStatus } from '../utils/getOnboardingStatus'; -export const useOnboardingStatus = (): OnboardingStatus | undefined => { +export const useOnboardingStatus = (): OnboardingStep | null | undefined => { const currentUser = useRecoilValue(currentUserState); const isLoggedIn = useIsLogged(); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index b6f35ef4e2e..8b2e8ea2162 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -23,7 +23,7 @@ export const getOnboardingStatus = ({ currentUser: CurrentUser | null; }) => { if (!isLoggedIn) { - return OnboardingStatus.OngoingUserCreation; + return OnboardingStep.UserCreation; } // After SignInUp, the user should have a current workspace assigned. @@ -32,43 +32,5 @@ export const getOnboardingStatus = ({ return undefined; } - if (currentUser.onboardingStep === OnboardingStep.SubscriptionIncomplete) { - return OnboardingStatus.Incomplete; - } - - if (currentUser.onboardingStep === OnboardingStep.WorkspaceActivation) { - return OnboardingStatus.OngoingWorkspaceActivation; - } - - if (currentUser.onboardingStep === OnboardingStep.ProfileCreation) { - return OnboardingStatus.OngoingProfileCreation; - } - - if (currentUser.onboardingStep === OnboardingStep.SyncEmail) { - return OnboardingStatus.OngoingSyncEmail; - } - - if (currentUser.onboardingStep === OnboardingStep.InviteTeam) { - return OnboardingStatus.OngoingInviteTeam; - } - - if (currentUser.onboardingStep === OnboardingStep.SubscriptionCanceled) { - return OnboardingStatus.Canceled; - } - - if (currentUser.onboardingStep === OnboardingStep.SubscriptionPastDue) { - return OnboardingStatus.PastDue; - } - - if (currentUser.onboardingStep === OnboardingStep.SubscriptionUnpaid) { - return OnboardingStatus.Unpaid; - } - - if ( - currentUser.onboardingStep === OnboardingStep.CompletedWithoutSubscription - ) { - return OnboardingStatus.CompletedWithoutSubscription; - } - - return OnboardingStatus.Completed; + return currentUser.onboardingStep; }; diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStep.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStep.ts index 42acf8feb0a..c0b5e3d89e1 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStep.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStep.ts @@ -1,6 +1,10 @@ -import { useRecoilCallback, useSetRecoilState } from 'recoil'; +import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; +import { + CurrentWorkspace, + currentWorkspaceState, +} from '@/auth/states/currentWorkspaceState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; @@ -9,13 +13,16 @@ import { OnboardingStep } from '~/generated/graphql'; const getNextOnboardingStep = ( currentOnboardingStep: OnboardingStep, workspaceMembers: WorkspaceMember[], + currentWorkspace: CurrentWorkspace | null, ) => { if (currentOnboardingStep === OnboardingStep.SyncEmail) { return workspaceMembers && workspaceMembers.length > 1 ? null : OnboardingStep.InviteTeam; } - return null; + return currentWorkspace?.currentBillingSubscription + ? OnboardingStep.Completed + : OnboardingStep.CompletedWithoutSubscription; }; export const useSetNextOnboardingStep = () => { @@ -23,6 +30,8 @@ export const useSetNextOnboardingStep = () => { const { records: workspaceMembers } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, }); + const currentWorkspace = useRecoilValue(currentWorkspaceState); + return useRecoilCallback( () => (currentOnboardingStep: OnboardingStep) => { setCurrentUser( @@ -32,10 +41,11 @@ export const useSetNextOnboardingStep = () => { onboardingStep: getNextOnboardingStep( currentOnboardingStep, workspaceMembers, + currentWorkspace, ), }) as any, ); }, - [setCurrentUser, workspaceMembers], + [setCurrentUser, workspaceMembers, currentWorkspace], ); }; diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index 375a5b7ff49..8a23fbfa8a9 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -6,6 +6,7 @@ import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; +import { OnboardingStep } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; jest.mock('@/auth/hooks/useOnboardingStatus'); @@ -39,257 +40,257 @@ const getResult = (isDefaultLayoutAuthModalVisible = true) => // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStatus.Incomplete, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingUserCreation, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingWorkspaceActivation, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingProfileCreation, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingSyncEmail, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.OngoingInviteTeam, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionIncomplete, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.UserCreation, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.WorkspaceActivation, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.ProfileCreation, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.SyncEmail, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.InviteTeam, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.Verify, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Invite, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.Canceled, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.Unpaid, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.PastDue, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionCanceled, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionUnpaid, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.SubscriptionPastDue, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.Completed, res: true }, + { loc: AppPath.Invite, status: OnboardingStep.CompletedWithoutSubscription, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Canceled, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Unpaid, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.PastDue, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionCanceled, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionUnpaid, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionPastDue, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.Completed, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStep.CompletedWithoutSubscription, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Canceled, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionCanceled, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.PlanRequired, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.PlanRequired, status: OnboardingStep.CompletedWithoutSubscription, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.Index, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.Index, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.Index, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.Index, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.Index, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.Index, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.Index, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.Index, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.Authorize, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.Authorize, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.Authorize, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.Authorize, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.Authorize, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.Authorize, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.Authorize, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStep.CompletedWithoutSubscription, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.Incomplete, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.Canceled, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.Unpaid, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.PastDue, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingUserCreation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingWorkspaceActivation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingProfileCreation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingSyncEmail, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.OngoingInviteTeam, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionIncomplete, res: true }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionCanceled, res: false }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionUnpaid, res: false }, + { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionPastDue, res: false }, + { loc: AppPath.NotFound, status: OnboardingStep.UserCreation, res: true }, + { loc: AppPath.NotFound, status: OnboardingStep.WorkspaceActivation, res: true }, + { loc: AppPath.NotFound, status: OnboardingStep.ProfileCreation, res: true }, + { loc: AppPath.NotFound, status: OnboardingStep.SyncEmail, res: true }, + { loc: AppPath.NotFound, status: OnboardingStep.InviteTeam, res: true }, + { loc: AppPath.NotFound, status: OnboardingStep.Completed, res: false }, + { loc: AppPath.NotFound, status: OnboardingStep.CompletedWithoutSubscription, res: false }, ]; describe('useShowAuthModal', () => { @@ -308,13 +309,13 @@ describe('useShowAuthModal', () => { describe('test with token validation loading', () => { it(`with appPath ${AppPath.Invite} and isDefaultLayoutAuthModalVisible=false`, () => { - setupMockOnboardingStatus(OnboardingStatus.Completed); + setupMockOnboardingStatus(OnboardingStep.Completed); setupMockIsMatchingLocation(AppPath.Invite); const { result } = getResult(false); expect(result.current).toBeFalsy(); }); it(`with appPath ${AppPath.ResetPassword} and isDefaultLayoutAuthModalVisible=false`, () => { - setupMockOnboardingStatus(OnboardingStatus.Completed); + setupMockOnboardingStatus(OnboardingStep.Completed); setupMockIsMatchingLocation(AppPath.ResetPassword); const { result } = getResult(false); expect(result.current).toBeFalsy(); diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index 47a59bc29b8..0ef2ee6e03d 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -2,10 +2,11 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; -import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; +import { OnboardingStep } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; +import { isDefined } from '~/utils/isDefined'; export const useShowAuthModal = () => { const isMatchingLocation = useIsMatchingLocation(); @@ -24,19 +25,25 @@ export const useShowAuthModal = () => { return isDefaultLayoutAuthModalVisible; } if ( - OnboardingStatus.Incomplete === onboardingStatus || - OnboardingStatus.OngoingUserCreation === onboardingStatus || - OnboardingStatus.OngoingProfileCreation === onboardingStatus || - OnboardingStatus.OngoingWorkspaceActivation === onboardingStatus || - OnboardingStatus.OngoingSyncEmail === onboardingStatus || - OnboardingStatus.OngoingInviteTeam === onboardingStatus + isDefined(onboardingStatus) && + [ + OnboardingStep.SubscriptionIncomplete, + OnboardingStep.UserCreation, + OnboardingStep.ProfileCreation, + OnboardingStep.WorkspaceActivation, + OnboardingStep.SyncEmail, + OnboardingStep.InviteTeam, + ].includes(onboardingStatus) ) { return true; } if (isMatchingLocation(AppPath.PlanRequired)) { return ( - OnboardingStatus.CompletedWithoutSubscription === onboardingStatus || - OnboardingStatus.Canceled === onboardingStatus + isDefined(onboardingStatus) && + [ + OnboardingStep.CompletedWithoutSubscription, + OnboardingStep.SubscriptionCanceled, + ].includes(onboardingStatus) ); } return false; diff --git a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx index 69b78240e36..fbe8da47b96 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import styled from '@emotion/styled'; import { zodResolver } from '@hookform/resolvers/zod'; -import { useRecoilState } from 'recoil'; +import { useRecoilState, useSetRecoilState } from 'recoil'; import { Key } from 'ts-key-enum'; import { H2Title } from 'twenty-ui'; import { z } from 'zod'; @@ -12,7 +12,6 @@ import { Title } from '@/auth/components/Title'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; -import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader'; @@ -61,7 +60,7 @@ export const CreateProfile = () => { const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState( currentWorkspaceMemberState, ); - const [currentUser, setCurrentUser] = useRecoilState(currentUserState); + const setCurrentUser = useSetRecoilState(currentUserState); const { updateOneRecord } = useUpdateOneRecord({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, @@ -148,7 +147,7 @@ export const CreateProfile = () => { PageHotkeyScope.CreateProfile, ); - if (onboardingStatus !== OnboardingStatus.OngoingProfileCreation) { + if (onboardingStatus !== OnboardingStep.ProfileCreation) { return null; } diff --git a/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx b/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx index 5d37f13d27c..307ea4c856f 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx @@ -11,7 +11,6 @@ import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState'; -import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries'; import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; @@ -20,7 +19,10 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; -import { useActivateWorkspaceMutation } from '~/generated/graphql'; +import { + OnboardingStep, + useActivateWorkspaceMutation, +} from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; const StyledContentContainer = styled.div` @@ -105,7 +107,7 @@ export const CreateWorkspace = () => { } }; - if (onboardingStatus !== OnboardingStatus.OngoingWorkspaceActivation) { + if (onboardingStatus !== OnboardingStep.WorkspaceActivation) { return null; } diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index 77763f68bce..caf72d83f05 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -12,7 +12,6 @@ import { import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SupportChat } from '@/support/components/SupportChat'; @@ -26,6 +25,7 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer' import { Section } from '@/ui/layout/section/components/Section'; import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { + OnboardingStep, useBillingPortalSessionQuery, useUpdateBillingSubscriptionMutation, } from '~/generated/graphql'; @@ -87,21 +87,21 @@ export const SettingsBilling = () => { loading || !isDefined(data) || !isDefined(data.billingPortalSession.url); const switchIntervalButtonDisabled = - onboardingStatus !== OnboardingStatus.Completed; + onboardingStatus !== OnboardingStep.Completed; const cancelPlanButtonDisabled = billingPortalButtonDisabled || - onboardingStatus !== OnboardingStatus.Completed; + onboardingStatus !== OnboardingStep.Completed; const displayPaymentFailInfo = - onboardingStatus === OnboardingStatus.PastDue || - onboardingStatus === OnboardingStatus.Unpaid; + onboardingStatus === OnboardingStep.SubscriptionPastDue || + onboardingStatus === OnboardingStep.SubscriptionUnpaid; const displaySubscriptionCanceledInfo = - onboardingStatus === OnboardingStatus.Canceled; + onboardingStatus === OnboardingStep.SubscriptionCanceled; const displaySubscribeInfo = - onboardingStatus === OnboardingStatus.CompletedWithoutSubscription; + onboardingStatus === OnboardingStep.CompletedWithoutSubscription; const openBillingPortal = () => { if (isDefined(data) && isDefined(data.billingPortalSession.url)) { diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts index 20b72a8c33d..518d1a86c2d 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts @@ -3,6 +3,7 @@ export enum OnboardingStep { SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED', SUBSCRIPTION_PAST_DUE = 'SUBSCRIPTION_PAST_DUE', SUBSCRIPTION_UNPAID = 'SUBSCRIPTION_UNPAID', + USER_CREATION = 'USER_CREATION', //Don't remove as used in front graphql generated file WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', From 405228e3d05956beb6d2949d5e6d48b43c89b144 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 13:16:02 +0200 Subject: [PATCH 09/47] Remove front onboardingStatus enum --- .../src/generated/graphql.tsx | 197 +++++--- .../src/generated-metadata/graphql.ts | 28 +- .../twenty-front/src/generated/graphql.tsx | 16 +- ...sePageChangeEffectNavigateLocation.test.ts | 465 +++++++++-------- .../usePageChangeEffectNavigateLocation.ts | 22 +- .../__test__/useOnboardingStatus.test.ts | 22 +- .../modules/auth/hooks/useOnboardingStatus.ts | 4 +- .../modules/auth/states/currentUserState.ts | 2 +- .../__test__/getOnboardingStatus.test.ts | 24 +- .../modules/auth/utils/getOnboardingStatus.ts | 20 +- ...gStep.ts => useSetNextOnboardingStatus.ts} | 22 +- .../hooks/__tests__/useShowAuthModal.test.tsx | 469 +++++++++--------- .../ui/layout/hooks/useShowAuthModal.ts | 18 +- .../graphql/fragments/userQueryFragment.ts | 2 +- .../src/pages/onboarding/CreateProfile.tsx | 6 +- .../src/pages/onboarding/CreateWorkspace.tsx | 4 +- .../src/pages/onboarding/InviteTeam.tsx | 15 +- .../src/pages/onboarding/SyncEmails.tsx | 14 +- .../__stories__/InviteTeam.stories.tsx | 4 +- .../__stories__/SyncEmails.stories.tsx | 4 +- .../src/pages/settings/SettingsBilling.tsx | 14 +- .../src/testing/mock-data/users.ts | 10 +- ...step.enum.ts => onboarding-status.enum.ts} | 2 +- .../onboarding/onboarding.service.ts | 32 +- .../engine/core-modules/user/user.entity.ts | 12 +- .../engine/core-modules/user/user.resolver.ts | 10 +- 26 files changed, 744 insertions(+), 694 deletions(-) rename packages/twenty-front/src/modules/onboarding/hooks/{useSetNextOnboardingStep.ts => useSetNextOnboardingStatus.ts} (70%) rename packages/twenty-server/src/engine/core-modules/onboarding/enums/{onboarding-step.enum.ts => onboarding-status.enum.ts} (94%) diff --git a/packages/twenty-chrome-extension/src/generated/graphql.tsx b/packages/twenty-chrome-extension/src/generated/graphql.tsx index 69e043e63d6..527fa6ff6ad 100644 --- a/packages/twenty-chrome-extension/src/generated/graphql.tsx +++ b/packages/twenty-chrome-extension/src/generated/graphql.tsx @@ -74,7 +74,7 @@ export type ActivityActivityTargetsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -86,7 +86,7 @@ export type ActivityAttachmentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -98,7 +98,7 @@ export type ActivityCommentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** An activity */ @@ -1009,7 +1009,7 @@ export type CalendarChannelCalendarChannelEventAssociationsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** Calendar Channels */ @@ -1296,7 +1296,7 @@ export type CalendarEventCalendarChannelEventAssociationsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -1308,7 +1308,7 @@ export type CalendarEventCalendarEventParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** Calendar events */ @@ -1786,6 +1786,7 @@ export type Company = { idealCustomerProfile?: Maybe; /** The company Linkedin account */ linkedinLink?: Maybe; + multiSelect?: Maybe>>; /** The company name */ name?: Maybe; /** Opportunities linked to the company. */ @@ -1811,7 +1812,7 @@ export type CompanyActivityTargetsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -1823,7 +1824,7 @@ export type CompanyAttachmentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -1835,7 +1836,7 @@ export type CompanyFavoritesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -1847,7 +1848,7 @@ export type CompanyOpportunitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -1859,7 +1860,7 @@ export type CompanyPeopleArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -1871,7 +1872,7 @@ export type CompanyTimelineActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** A company */ @@ -1902,6 +1903,7 @@ export type CompanyCreateInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; + multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; /** Company record position */ @@ -1939,6 +1941,7 @@ export type CompanyFilterInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; + multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; not?: InputMaybe; @@ -1951,6 +1954,24 @@ export type CompanyFilterInput = { xLink?: InputMaybe; }; +export enum CompanyMultiSelectEnum { + /** Option 1 */ + Option_1 = 'OPTION_1', + /** Option 2 */ + Option_2 = 'OPTION_2', + /** Option 3 */ + Option_3 = 'OPTION_3', + /** Option 4 */ + Option_4 = 'OPTION_4' +} + +export type CompanyMultiSelectEnumFilter = { + eq?: InputMaybe; + in?: InputMaybe>>; + is?: InputMaybe; + neq?: InputMaybe; +}; + /** A company */ export type CompanyOrderByInput = { /** Your team member responsible for managing the company account id foreign key */ @@ -1971,6 +1992,7 @@ export type CompanyOrderByInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; + multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; /** Company record position */ @@ -2001,6 +2023,7 @@ export type CompanyUpdateInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; + multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; /** Company record position */ @@ -2050,7 +2073,7 @@ export type ConnectedAccountCalendarChannelsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -2062,7 +2085,7 @@ export type ConnectedAccountMessageChannelsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** A connected account */ @@ -2597,7 +2620,7 @@ export type MessageMessageChannelMessageAssociationsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -2609,7 +2632,7 @@ export type MessageMessageParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** Message Channels */ @@ -2659,7 +2682,7 @@ export type MessageChannelMessageChannelMessageAssociationsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** Message Channels */ @@ -3300,7 +3323,7 @@ export type MessageThreadMessageChannelMessageAssociationsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -3312,7 +3335,7 @@ export type MessageThreadMessagesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** Message Thread */ @@ -3517,7 +3540,9 @@ export type Mutation = { deleteWebhooks?: Maybe>; deleteWorkspaceMember?: Maybe; deleteWorkspaceMembers?: Maybe>; + disablePostgresProxy: PostgresCredentials; emailPasswordResetLink: EmailPasswordResetLink; + enablePostgresProxy: PostgresCredentials; exchangeAuthorizationCode: ExchangeAuthCode; executeQuickActionOnActivity?: Maybe; executeQuickActionOnActivityTarget?: Maybe; @@ -4803,10 +4828,19 @@ export type ObjectFieldsConnection = { pageInfo: PageInfo; }; -/** Onboarding step */ -export enum OnboardingStep { +/** Onboarding status */ +export enum OnboardingStatus { + Completed = 'COMPLETED', + CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', - SyncEmail = 'SYNC_EMAIL' + ProfileCreation = 'PROFILE_CREATION', + SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', + SubscriptionIncomplete = 'SUBSCRIPTION_INCOMPLETE', + SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', + SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', + SyncEmail = 'SYNC_EMAIL', + UserCreation = 'USER_CREATION', + WorkspaceActivation = 'WORKSPACE_ACTIVATION' } export type OnboardingStepSuccess = { @@ -4861,7 +4895,7 @@ export type OpportunityActivityTargetsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -4873,7 +4907,7 @@ export type OpportunityAttachmentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -4885,7 +4919,7 @@ export type OpportunityFavoritesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -4897,7 +4931,7 @@ export type OpportunityTimelineActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** An opportunity */ @@ -5120,7 +5154,7 @@ export type PersonActivityTargetsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5132,7 +5166,7 @@ export type PersonAttachmentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5144,7 +5178,7 @@ export type PersonCalendarEventParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5156,7 +5190,7 @@ export type PersonFavoritesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5168,7 +5202,7 @@ export type PersonMessageParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5180,7 +5214,7 @@ export type PersonPointOfContactForOpportunitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5192,7 +5226,7 @@ export type PersonTimelineActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** A person */ @@ -5332,6 +5366,13 @@ export type PersonUpdateInput = { xLink?: InputMaybe; }; +export type PostgresCredentials = { + id: Scalars['UUID']; + password: Scalars['String']; + user: Scalars['String']; + workspaceId: Scalars['String']; +}; + export type ProductPriceEntity = { created: Scalars['Float']; recurringInterval: Scalars['String']; @@ -5394,6 +5435,7 @@ export type Query = { favoriteDuplicates?: Maybe; favorites?: Maybe; findWorkspaceFromInviteHash: Workspace; + getPostgresCredentials?: Maybe; getProductPrices: ProductPricesEntity; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; @@ -5454,7 +5496,7 @@ export type QueryActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5487,7 +5529,7 @@ export type QueryActivityTargetsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5509,7 +5551,7 @@ export type QueryApiKeysArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5531,7 +5573,7 @@ export type QueryAttachmentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5553,7 +5595,7 @@ export type QueryAuditLogsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5580,7 +5622,7 @@ export type QueryBlocklistsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5613,7 +5655,7 @@ export type QueryCalendarChannelEventAssociationsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5624,7 +5666,7 @@ export type QueryCalendarChannelsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5657,7 +5699,7 @@ export type QueryCalendarEventParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5668,7 +5710,7 @@ export type QueryCalendarEventsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5701,7 +5743,7 @@ export type QueryCommentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5712,7 +5754,7 @@ export type QueryCompaniesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5745,7 +5787,7 @@ export type QueryConnectedAccountsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5767,7 +5809,7 @@ export type QueryFavoritesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5843,7 +5885,7 @@ export type QueryMessageChannelMessageAssociationsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5854,7 +5896,7 @@ export type QueryMessageChannelsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5882,7 +5924,7 @@ export type QueryMessageParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5904,7 +5946,7 @@ export type QueryMessageThreadsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5915,7 +5957,7 @@ export type QueryMessagesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5926,7 +5968,7 @@ export type QueryOpportunitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5948,7 +5990,7 @@ export type QueryPeopleArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -5970,7 +6012,7 @@ export type QueryTimelineActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6019,7 +6061,7 @@ export type QueryViewFieldsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6041,7 +6083,7 @@ export type QueryViewFiltersArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6063,7 +6105,7 @@ export type QueryViewSortsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6074,7 +6116,7 @@ export type QueryViewsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6096,7 +6138,7 @@ export type QueryWebhooksArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6118,7 +6160,7 @@ export type QueryWorkspaceMembersArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; export type RawJsonFilter = { @@ -6232,7 +6274,6 @@ export type Support = { }; export type Telemetry = { - anonymizationEnabled: Scalars['Boolean']; enabled: Scalars['Boolean']; }; @@ -6534,7 +6575,7 @@ export type User = { firstName: Scalars['String']; id: Scalars['UUID']; lastName: Scalars['String']; - onboardingStep?: Maybe; + onboardingStatus?: Maybe; passwordHash?: Maybe; /** @deprecated field migrated into the AppTokens Table ref: https://github.com/twentyhq/twenty/issues/5021 */ passwordResetToken?: Maybe; @@ -6623,7 +6664,7 @@ export type ViewViewFieldsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6635,7 +6676,7 @@ export type ViewViewFiltersArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -6647,7 +6688,7 @@ export type ViewViewSortsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** (System) Views */ @@ -7304,7 +7345,7 @@ export type WorkspaceMemberAccountOwnerForCompaniesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7316,7 +7357,7 @@ export type WorkspaceMemberAssignedActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7328,7 +7369,7 @@ export type WorkspaceMemberAuditLogsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7340,7 +7381,7 @@ export type WorkspaceMemberAuthoredActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7352,7 +7393,7 @@ export type WorkspaceMemberAuthoredAttachmentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7364,7 +7405,7 @@ export type WorkspaceMemberAuthoredCommentsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7376,7 +7417,7 @@ export type WorkspaceMemberBlocklistArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7388,7 +7429,7 @@ export type WorkspaceMemberCalendarEventParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7400,7 +7441,7 @@ export type WorkspaceMemberConnectedAccountsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7412,7 +7453,7 @@ export type WorkspaceMemberFavoritesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7424,7 +7465,7 @@ export type WorkspaceMemberMessageParticipantsArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; @@ -7436,7 +7477,7 @@ export type WorkspaceMemberTimelineActivitiesArgs = { first?: InputMaybe; last?: InputMaybe; limit?: InputMaybe; - orderBy?: InputMaybe; + orderBy?: InputMaybe>>; }; /** A workspace member */ diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 05155eaa108..0481e158764 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -398,7 +398,9 @@ export type Mutation = { deleteOneRelation: Relation; deleteOneRemoteServer: RemoteServer; deleteUser: User; + disablePostgresProxy: PostgresCredentials; emailPasswordResetLink: EmailPasswordResetLink; + enablePostgresProxy: PostgresCredentials; exchangeAuthorizationCode: ExchangeAuthCode; generateApiKeyToken: ApiKeyToken; generateJWT: AuthTokens; @@ -636,10 +638,19 @@ export type ObjectFieldsConnection = { pageInfo: PageInfo; }; -/** Onboarding step */ -export enum OnboardingStep { +/** Onboarding status */ +export enum OnboardingStatus { + Completed = 'COMPLETED', + CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', - SyncEmail = 'SYNC_EMAIL' + ProfileCreation = 'PROFILE_CREATION', + SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', + SubscriptionIncomplete = 'SUBSCRIPTION_INCOMPLETE', + SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', + SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', + SyncEmail = 'SYNC_EMAIL', + UserCreation = 'USER_CREATION', + WorkspaceActivation = 'WORKSPACE_ACTIVATION' } export type OnboardingStepSuccess = { @@ -660,6 +671,14 @@ export type PageInfo = { startCursor?: Maybe; }; +export type PostgresCredentials = { + __typename?: 'PostgresCredentials'; + id: Scalars['UUID']['output']; + password: Scalars['String']['output']; + user: Scalars['String']['output']; + workspaceId: Scalars['String']['output']; +}; + export type ProductPriceEntity = { __typename?: 'ProductPriceEntity'; created: Scalars['Float']['output']; @@ -688,6 +707,7 @@ export type Query = { findManyRemoteServersByType: Array; findOneRemoteServerById: RemoteServer; findWorkspaceFromInviteHash: Workspace; + getPostgresCredentials?: Maybe; getProductPrices: ProductPricesEntity; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; @@ -1084,7 +1104,7 @@ export type User = { firstName: Scalars['String']['output']; id: Scalars['UUID']['output']; lastName: Scalars['String']['output']; - onboardingStep?: Maybe; + onboardingStatus?: Maybe; passwordHash?: Maybe; /** @deprecated field migrated into the AppTokens Table ref: https://github.com/twentyhq/twenty/issues/5021 */ passwordResetToken?: Maybe; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 7b8b054f090..ca2d43f6918 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -461,8 +461,8 @@ export type ObjectFieldsConnection = { pageInfo: PageInfo; }; -/** Onboarding step */ -export enum OnboardingStep { +/** Onboarding status */ +export enum OnboardingStatus { Completed = 'COMPLETED', CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', @@ -830,7 +830,7 @@ export type User = { firstName: Scalars['String']; id: Scalars['UUID']; lastName: Scalars['String']; - onboardingStep?: Maybe; + onboardingStatus?: Maybe; passwordHash?: Maybe; /** @deprecated field migrated into the AppTokens Table ref: https://github.com/twentyhq/twenty/issues/5021 */ passwordResetToken?: Maybe; @@ -1159,7 +1159,7 @@ export type ImpersonateMutationVariables = Exact<{ }>; -export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStep?: OnboardingStep | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type RenewTokenMutationVariables = Exact<{ appToken: Scalars['String']; @@ -1191,7 +1191,7 @@ export type VerifyMutationVariables = Exact<{ }>; -export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStep?: OnboardingStep | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type CheckUserExistsQueryVariables = Exact<{ email: Scalars['String']; @@ -1245,7 +1245,7 @@ export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string] export type SkipSyncEmailOnboardingStepMutation = { __typename?: 'Mutation', skipSyncEmailOnboardingStep: { __typename?: 'OnboardingStepSuccess', success: boolean } }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStep?: OnboardingStep | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }; export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>; @@ -1262,7 +1262,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, canImpersonate: boolean, supportUserHash?: string | null, onboardingStep?: OnboardingStep | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } }; export type AddUserToWorkspaceMutationVariables = Exact<{ inviteHash: Scalars['String']; @@ -1406,7 +1406,7 @@ export const UserQueryFragmentFragmentDoc = gql` email canImpersonate supportUserHash - onboardingStep + onboardingStatus workspaceMember { id name { diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index ca8181e4312..0d15a55dc15 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -1,7 +1,6 @@ import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; -import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { AppPath } from '@/types/AppPath'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation'; @@ -29,257 +28,257 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({ // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Verify, status: OnboardingStep.UserCreation, res: undefined }, - { loc: AppPath.Verify, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Verify, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Verify, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Verify, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Verify, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.Verify, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Verify, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.Verify, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Verify, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Verify, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Verify, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.SignInUp, status: OnboardingStep.UserCreation, res: undefined }, - { loc: AppPath.SignInUp, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SignInUp, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SignInUp, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SignInUp, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SignInUp, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SignInUp, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.SignInUp, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SignInUp, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SignInUp, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionIncomplete, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionCanceled, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.UserCreation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.WorkspaceActivation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.ProfileCreation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.SyncEmail, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.InviteTeam, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionIncomplete, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionCanceled, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.UserCreation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.WorkspaceActivation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.ProfileCreation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SyncEmail, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.InviteTeam, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.WorkspaceActivation, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.CreateProfile, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.CreateProfile, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.CreateProfile, status: OnboardingStep.ProfileCreation, res: undefined }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateProfile, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateProfile, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.SyncEmails, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.SyncEmails, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SyncEmails, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SyncEmail, res: undefined }, - { loc: AppPath.SyncEmails, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SyncEmails, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.InviteTeam, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.InviteTeam, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.InviteTeam, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.InviteTeam, status: OnboardingStep.InviteTeam, res: undefined }, - { loc: AppPath.InviteTeam, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionIncomplete, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionCanceled, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequired, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.PlanRequired, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequired, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequired, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.WorkspaceActivation, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.Index, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Index, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Index, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Index, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Index, status: OnboardingStep.Completed, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStep.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, + { loc: AppPath.Index, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Index, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Index, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Index, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Index, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Index, status: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.TasksPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.TasksPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.TasksPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.TasksPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.TasksPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.TasksPage, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.TasksPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.TasksPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.TasksPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.TasksPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.TasksPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionCanceled, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.Impersonate, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Impersonate, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Impersonate, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Impersonate, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Impersonate, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Impersonate, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Impersonate, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Impersonate, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Impersonate, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.Authorize, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Authorize, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Authorize, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Authorize, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Authorize, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Authorize, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Authorize, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Authorize, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Authorize, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Authorize, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionIncomplete, res: AppPath.PlanRequired }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionPastDue, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStep.UserCreation, res: AppPath.SignInUp }, - { loc: AppPath.NotFound, status: OnboardingStep.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFound, status: OnboardingStep.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFound, status: OnboardingStep.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFound, status: OnboardingStep.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFound, status: OnboardingStep.Completed, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStep.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.NotFound, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.NotFound, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFound, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFound, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFound, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFound, status: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFound, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, ]; describe('usePageChangeEffectNavigateLocation', () => { diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index e6c4aed2db2..b0d5719da32 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -1,7 +1,7 @@ import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { isDefined } from '~/utils/isDefined'; @@ -34,14 +34,14 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStep.UserCreation && + onboardingStatus === OnboardingStatus.UserCreation && !isMatchingOngoingUserCreationRoute ) { return AppPath.SignInUp; } if ( - onboardingStatus === OnboardingStep.SubscriptionIncomplete && + onboardingStatus === OnboardingStatus.SubscriptionIncomplete && !isMatchingLocation(AppPath.PlanRequired) ) { return AppPath.PlanRequired; @@ -50,8 +50,8 @@ export const usePageChangeEffectNavigateLocation = () => { if ( isDefined(onboardingStatus) && [ - OnboardingStep.SubscriptionUnpaid, - OnboardingStep.SubscriptionCanceled, + OnboardingStatus.SubscriptionUnpaid, + OnboardingStatus.SubscriptionCanceled, ].includes(onboardingStatus) && !( isMatchingLocation(AppPath.SettingsCatchAll) || @@ -64,7 +64,7 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStep.WorkspaceActivation && + onboardingStatus === OnboardingStatus.WorkspaceActivation && !isMatchingLocation(AppPath.CreateWorkspace) && !isMatchingLocation(AppPath.PlanRequiredSuccess) ) { @@ -72,35 +72,35 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStep.ProfileCreation && + onboardingStatus === OnboardingStatus.ProfileCreation && !isMatchingLocation(AppPath.CreateProfile) ) { return AppPath.CreateProfile; } if ( - onboardingStatus === OnboardingStep.SyncEmail && + onboardingStatus === OnboardingStatus.SyncEmail && !isMatchingLocation(AppPath.SyncEmails) ) { return AppPath.SyncEmails; } if ( - onboardingStatus === OnboardingStep.InviteTeam && + onboardingStatus === OnboardingStatus.InviteTeam && !isMatchingLocation(AppPath.InviteTeam) ) { return AppPath.InviteTeam; } if ( - onboardingStatus === OnboardingStep.Completed && + onboardingStatus === OnboardingStatus.Completed && isMatchingOnboardingRoute ) { return defaultHomePagePath; } if ( - onboardingStatus === OnboardingStep.CompletedWithoutSubscription && + onboardingStatus === OnboardingStatus.CompletedWithoutSubscription && isMatchingOnboardingRoute && !isMatchingLocation(AppPath.PlanRequired) ) { diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index 37000091791..d804d7f8734 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -10,7 +10,7 @@ import { } from '@/auth/states/currentWorkspaceState'; import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState'; import { tokenPairState } from '@/auth/states/tokenPairState'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; const tokenPair = { accessToken: { token: 'accessToken', expiresAt: 'expiresAt' }, @@ -25,7 +25,7 @@ const currentUser = { email: 'test@test', supportUserHash: '1', canImpersonate: false, - onboardingStep: null, + onboardingStatus: null, } as CurrentUser; const currentWorkspace = { activationStatus: 'active', @@ -76,7 +76,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.SubscriptionIncomplete, + onboardingStatus: OnboardingStatus.SubscriptionIncomplete, }); setCurrentWorkspace(currentWorkspace); }); @@ -93,7 +93,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.SubscriptionCanceled, + onboardingStatus: OnboardingStatus.SubscriptionCanceled, }); setCurrentWorkspace(currentWorkspace); }); @@ -110,7 +110,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.WorkspaceActivation, + onboardingStatus: OnboardingStatus.WorkspaceActivation, }); setCurrentWorkspace(currentWorkspace); }); @@ -129,7 +129,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.ProfileCreation, + onboardingStatus: OnboardingStatus.ProfileCreation, }); setCurrentWorkspace({ ...currentWorkspace, @@ -149,7 +149,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.SyncEmail, + onboardingStatus: OnboardingStatus.SyncEmail, }); setCurrentWorkspace({ ...currentWorkspace, @@ -169,7 +169,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.InviteTeam, + onboardingStatus: OnboardingStatus.InviteTeam, }); setCurrentWorkspace({ ...currentWorkspace, @@ -206,7 +206,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.SubscriptionPastDue, + onboardingStatus: OnboardingStatus.SubscriptionPastDue, }); setCurrentWorkspace(currentWorkspace); }); @@ -223,7 +223,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.SubscriptionUnpaid, + onboardingStatus: OnboardingStatus.SubscriptionUnpaid, }); setCurrentWorkspace(currentWorkspace); }); @@ -240,7 +240,7 @@ describe('useOnboardingStatus', () => { setTokenPair(tokenPair); setCurrentUser({ ...currentUser, - onboardingStep: OnboardingStep.CompletedWithoutSubscription, + onboardingStatus: OnboardingStatus.CompletedWithoutSubscription, }); setCurrentWorkspace(currentWorkspace); }); diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts index 5c9d394f1cd..6c9d515731a 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts @@ -1,12 +1,12 @@ import { useRecoilValue } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { useIsLogged } from '../hooks/useIsLogged'; import { getOnboardingStatus } from '../utils/getOnboardingStatus'; -export const useOnboardingStatus = (): OnboardingStep | null | undefined => { +export const useOnboardingStatus = (): OnboardingStatus | null | undefined => { const currentUser = useRecoilValue(currentUserState); const isLoggedIn = useIsLogged(); diff --git a/packages/twenty-front/src/modules/auth/states/currentUserState.ts b/packages/twenty-front/src/modules/auth/states/currentUserState.ts index 07efc7dfb1e..2aab02507ff 100644 --- a/packages/twenty-front/src/modules/auth/states/currentUserState.ts +++ b/packages/twenty-front/src/modules/auth/states/currentUserState.ts @@ -4,7 +4,7 @@ import { User } from '~/generated/graphql'; export type CurrentUser = Pick< User, - 'id' | 'email' | 'supportUserHash' | 'canImpersonate' | 'onboardingStep' + 'id' | 'email' | 'supportUserHash' | 'canImpersonate' | 'onboardingStatus' >; export const currentUserState = createState({ diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts index 836067fd273..4993c5f266b 100644 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts @@ -1,5 +1,5 @@ import { CurrentUser } from '@/auth/states/currentUserState'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { getOnboardingStatus } from '../getOnboardingStatus'; @@ -13,77 +13,77 @@ describe('getOnboardingStatus', () => { const ongoingWorkspaceActivation = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.WorkspaceActivation, + onboardingStatus: OnboardingStatus.WorkspaceActivation, } as CurrentUser, }); const ongoingProfileCreation = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.ProfileCreation, + onboardingStatus: OnboardingStatus.ProfileCreation, } as CurrentUser, }); const ongoingSyncEmail = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.SyncEmail, + onboardingStatus: OnboardingStatus.SyncEmail, } as CurrentUser, }); const ongoingInviteTeam = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.InviteTeam, + onboardingStatus: OnboardingStatus.InviteTeam, } as CurrentUser, }); const completed = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: null, + onboardingStatus: null, } as CurrentUser, }); const incomplete = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.SubscriptionIncomplete, + onboardingStatus: OnboardingStatus.SubscriptionIncomplete, } as CurrentUser, }); const incompleteButBillingDisabled = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: null, + onboardingStatus: null, } as CurrentUser, }); const canceled = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.SubscriptionCanceled, + onboardingStatus: OnboardingStatus.SubscriptionCanceled, } as CurrentUser, }); const past_due = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.SubscriptionPastDue, + onboardingStatus: OnboardingStatus.SubscriptionPastDue, } as CurrentUser, }); const unpaid = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.SubscriptionUnpaid, + onboardingStatus: OnboardingStatus.SubscriptionUnpaid, } as CurrentUser, }); const completeWithoutSubscription = getOnboardingStatus({ isLoggedIn: true, currentUser: { - onboardingStep: OnboardingStep.CompletedWithoutSubscription, + onboardingStatus: OnboardingStatus.CompletedWithoutSubscription, } as CurrentUser, }); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index 8b2e8ea2162..d58e5f7651c 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -1,19 +1,5 @@ import { CurrentUser } from '@/auth/states/currentUserState'; -import { OnboardingStep } from '~/generated/graphql'; - -export enum OnboardingStatus { - Incomplete = 'incomplete', - Canceled = 'canceled', - Unpaid = 'unpaid', - PastDue = 'past_due', - OngoingUserCreation = 'ongoing_user_creation', - OngoingWorkspaceActivation = 'ongoing_workspace_activation', - OngoingProfileCreation = 'ongoing_profile_creation', - OngoingSyncEmail = 'ongoing_sync_email', - OngoingInviteTeam = 'ongoing_invite_team', - Completed = 'completed', - CompletedWithoutSubscription = 'completed_without_subscription', -} +import { OnboardingStatus } from '~/generated/graphql'; export const getOnboardingStatus = ({ isLoggedIn, @@ -23,7 +9,7 @@ export const getOnboardingStatus = ({ currentUser: CurrentUser | null; }) => { if (!isLoggedIn) { - return OnboardingStep.UserCreation; + return OnboardingStatus.UserCreation; } // After SignInUp, the user should have a current workspace assigned. @@ -32,5 +18,5 @@ export const getOnboardingStatus = ({ return undefined; } - return currentUser.onboardingStep; + return currentUser.onboardingStatus; }; diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStep.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts similarity index 70% rename from packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStep.ts rename to packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index c0b5e3d89e1..8798a4f6095 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStep.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -8,24 +8,24 @@ import { import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; -const getNextOnboardingStep = ( - currentOnboardingStep: OnboardingStep, +const getNextOnboardingStatus = ( + currentOnboardingStatus: OnboardingStatus, workspaceMembers: WorkspaceMember[], currentWorkspace: CurrentWorkspace | null, ) => { - if (currentOnboardingStep === OnboardingStep.SyncEmail) { + if (currentOnboardingStatus === OnboardingStatus.SyncEmail) { return workspaceMembers && workspaceMembers.length > 1 ? null - : OnboardingStep.InviteTeam; + : OnboardingStatus.InviteTeam; } return currentWorkspace?.currentBillingSubscription - ? OnboardingStep.Completed - : OnboardingStep.CompletedWithoutSubscription; + ? OnboardingStatus.Completed + : OnboardingStatus.CompletedWithoutSubscription; }; -export const useSetNextOnboardingStep = () => { +export const useSetNextOnboardingStatus = () => { const setCurrentUser = useSetRecoilState(currentUserState); const { records: workspaceMembers } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, @@ -33,13 +33,13 @@ export const useSetNextOnboardingStep = () => { const currentWorkspace = useRecoilValue(currentWorkspaceState); return useRecoilCallback( - () => (currentOnboardingStep: OnboardingStep) => { + () => (currentOnboardingStatus: OnboardingStatus) => { setCurrentUser( (current) => ({ ...current, - onboardingStep: getNextOnboardingStep( - currentOnboardingStep, + onboardingStatus: getNextOnboardingStatus( + currentOnboardingStatus, workspaceMembers, currentWorkspace, ), diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index 8a23fbfa8a9..87f81bd88c1 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -2,11 +2,10 @@ import { renderHook } from '@testing-library/react'; import { RecoilRoot, useSetRecoilState } from 'recoil'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; -import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; jest.mock('@/auth/hooks/useOnboardingStatus'); @@ -40,257 +39,257 @@ const getResult = (isDefaultLayoutAuthModalVisible = true) => // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionIncomplete, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.UserCreation, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.WorkspaceActivation, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.ProfileCreation, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.SyncEmail, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.InviteTeam, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.Verify, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionIncomplete, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.UserCreation, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.WorkspaceActivation, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.ProfileCreation, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.SyncEmail, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.InviteTeam, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionCanceled, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionUnpaid, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.SubscriptionPastDue, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.Completed, res: true }, - { loc: AppPath.Invite, status: OnboardingStep.CompletedWithoutSubscription, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionCanceled, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionUnpaid, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SubscriptionPastDue, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.Completed, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStep.CompletedWithoutSubscription, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionCanceled, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStep.CompletedWithoutSubscription, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.Index, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.Index, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.Index, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.Index, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.Index, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.Index, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.Index, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.Index, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.Index, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Index, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Index, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Index, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Index, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Index, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.Authorize, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.Authorize, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.Authorize, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.Authorize, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.Authorize, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.Authorize, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Authorize, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Authorize, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Authorize, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Authorize, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionIncomplete, res: true }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionCanceled, res: false }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionUnpaid, res: false }, - { loc: AppPath.NotFound, status: OnboardingStep.SubscriptionPastDue, res: false }, - { loc: AppPath.NotFound, status: OnboardingStep.UserCreation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStep.WorkspaceActivation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStep.ProfileCreation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStep.SyncEmail, res: true }, - { loc: AppPath.NotFound, status: OnboardingStep.InviteTeam, res: true }, - { loc: AppPath.NotFound, status: OnboardingStep.Completed, res: false }, - { loc: AppPath.NotFound, status: OnboardingStep.CompletedWithoutSubscription, res: false }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: false }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: false }, + { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: false }, + { loc: AppPath.NotFound, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.NotFound, status: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.NotFound, status: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.NotFound, status: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.NotFound, status: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.NotFound, status: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFound, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, ]; describe('useShowAuthModal', () => { @@ -309,13 +308,13 @@ describe('useShowAuthModal', () => { describe('test with token validation loading', () => { it(`with appPath ${AppPath.Invite} and isDefaultLayoutAuthModalVisible=false`, () => { - setupMockOnboardingStatus(OnboardingStep.Completed); + setupMockOnboardingStatus(OnboardingStatus.Completed); setupMockIsMatchingLocation(AppPath.Invite); const { result } = getResult(false); expect(result.current).toBeFalsy(); }); it(`with appPath ${AppPath.ResetPassword} and isDefaultLayoutAuthModalVisible=false`, () => { - setupMockOnboardingStatus(OnboardingStep.Completed); + setupMockOnboardingStatus(OnboardingStatus.Completed); setupMockIsMatchingLocation(AppPath.ResetPassword); const { result } = getResult(false); expect(result.current).toBeFalsy(); diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index 0ef2ee6e03d..03cf5dfac99 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -4,7 +4,7 @@ import { useRecoilValue } from 'recoil'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { isDefined } from '~/utils/isDefined'; @@ -27,12 +27,12 @@ export const useShowAuthModal = () => { if ( isDefined(onboardingStatus) && [ - OnboardingStep.SubscriptionIncomplete, - OnboardingStep.UserCreation, - OnboardingStep.ProfileCreation, - OnboardingStep.WorkspaceActivation, - OnboardingStep.SyncEmail, - OnboardingStep.InviteTeam, + OnboardingStatus.SubscriptionIncomplete, + OnboardingStatus.UserCreation, + OnboardingStatus.ProfileCreation, + OnboardingStatus.WorkspaceActivation, + OnboardingStatus.SyncEmail, + OnboardingStatus.InviteTeam, ].includes(onboardingStatus) ) { return true; @@ -41,8 +41,8 @@ export const useShowAuthModal = () => { return ( isDefined(onboardingStatus) && [ - OnboardingStep.CompletedWithoutSubscription, - OnboardingStep.SubscriptionCanceled, + OnboardingStatus.CompletedWithoutSubscription, + OnboardingStatus.SubscriptionCanceled, ].includes(onboardingStatus) ); } diff --git a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts index 32d358c6457..d3a65b504d8 100644 --- a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts +++ b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts @@ -8,7 +8,7 @@ export const USER_QUERY_FRAGMENT = gql` email canImpersonate supportUserHash - onboardingStep + onboardingStatus workspaceMember { id name { diff --git a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx index fbe8da47b96..8b6f7f42189 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx @@ -22,7 +22,7 @@ import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; const StyledContentContainer = styled.div` width: 100%; @@ -117,7 +117,7 @@ export const CreateProfile = () => { (current) => ({ ...current, - onboardingStep: OnboardingStep.SyncEmail, + onboardingStatus: OnboardingStatus.SyncEmail, }) as any, ); } catch (error: any) { @@ -147,7 +147,7 @@ export const CreateProfile = () => { PageHotkeyScope.CreateProfile, ); - if (onboardingStatus !== OnboardingStep.ProfileCreation) { + if (onboardingStatus !== OnboardingStatus.ProfileCreation) { return null; } diff --git a/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx b/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx index 307ea4c856f..b14b02ebbfe 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx @@ -20,7 +20,7 @@ import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; import { - OnboardingStep, + OnboardingStatus, useActivateWorkspaceMutation, } from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; @@ -107,7 +107,7 @@ export const CreateWorkspace = () => { } }; - if (onboardingStatus !== OnboardingStep.WorkspaceActivation) { + if (onboardingStatus !== OnboardingStatus.WorkspaceActivation) { return null; } diff --git a/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx b/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx index 2395c4bb27d..93c13520447 100644 --- a/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx +++ b/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx @@ -17,7 +17,7 @@ import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { useSetNextOnboardingStep } from '@/onboarding/hooks/useSetNextOnboardingStep'; +import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { SeparatorLineText } from '@/ui/display/text/components/SeparatorLineText'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; @@ -27,7 +27,10 @@ import { MainButton } from '@/ui/input/button/components/MainButton'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; import { AnimatedTranslation } from '@/ui/utilities/animation/components/AnimatedTranslation'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; -import { OnboardingStep, useSendInviteLinkMutation } from '~/generated/graphql'; +import { + OnboardingStatus, + useSendInviteLinkMutation, +} from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; const StyledAnimatedContainer = styled.div` @@ -63,7 +66,7 @@ export const InviteTeam = () => { const theme = useTheme(); const { enqueueSnackBar } = useSnackBar(); const [sendInviteLink] = useSendInviteLinkMutation(); - const setNextOnboardingStep = useSetNextOnboardingStep(); + const setNextOnboardingStatus = useSetNextOnboardingStatus(); const currentUser = useRecoilValue(currentUserState); const currentWorkspace = useRecoilValue(currentWorkspaceState); const { @@ -133,7 +136,7 @@ export const InviteTeam = () => { ); const result = await sendInviteLink({ variables: { emails } }); - setNextOnboardingStep(OnboardingStep.InviteTeam); + setNextOnboardingStatus(OnboardingStatus.InviteTeam); if (isDefined(result.errors)) { throw result.errors; @@ -145,7 +148,7 @@ export const InviteTeam = () => { }); } }, - [enqueueSnackBar, sendInviteLink, setNextOnboardingStep], + [enqueueSnackBar, sendInviteLink, setNextOnboardingStatus], ); useScopedHotkeys( @@ -157,7 +160,7 @@ export const InviteTeam = () => { [handleSubmit], ); - if (currentUser?.onboardingStep !== OnboardingStep.InviteTeam) { + if (currentUser?.onboardingStatus !== OnboardingStatus.InviteTeam) { return <>; } diff --git a/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx b/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx index a52654701f3..9c3323bbf65 100644 --- a/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx +++ b/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx @@ -9,7 +9,7 @@ import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; import { currentUserState } from '@/auth/states/currentUserState'; import { OnboardingSyncEmailsSettingsCard } from '@/onboarding/components/OnboardingSyncEmailsSettingsCard'; -import { useSetNextOnboardingStep } from '@/onboarding/hooks/useSetNextOnboardingStep'; +import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus'; import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth'; import { AppPath } from '@/types/AppPath'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; @@ -19,7 +19,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { CalendarChannelVisibility, MessageChannelVisibility, - OnboardingStep, + OnboardingStatus, useSkipSyncEmailOnboardingStepMutation, } from '~/generated/graphql'; @@ -40,12 +40,12 @@ const StyledActionLinkContainer = styled.div` export const SyncEmails = () => { const theme = useTheme(); const { triggerGoogleApisOAuth } = useTriggerGoogleApisOAuth(); - const setNextOnboardingStep = useSetNextOnboardingStep(); + const setNextOnboardingStatus = useSetNextOnboardingStatus(); const currentUser = useRecoilValue(currentUserState); const [visibility, setVisibility] = useState( MessageChannelVisibility.ShareEverything, ); - const [skipSyncEmailOnboardingStepMutation] = + const [skipSyncEmailOnboardingStatusMutation] = useSkipSyncEmailOnboardingStepMutation(); const handleButtonClick = async () => { @@ -62,8 +62,8 @@ export const SyncEmails = () => { }; const continueWithoutSync = async () => { - await skipSyncEmailOnboardingStepMutation(); - setNextOnboardingStep(OnboardingStep.SyncEmail); + await skipSyncEmailOnboardingStatusMutation(); + setNextOnboardingStatus(OnboardingStatus.SyncEmail); }; useScopedHotkeys( @@ -75,7 +75,7 @@ export const SyncEmails = () => { [continueWithoutSync], ); - if (currentUser?.onboardingStep !== OnboardingStep.SyncEmail) { + if (currentUser?.onboardingStatus !== OnboardingStatus.SyncEmail) { return <>; } diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx index 799daa50737..850da79c5e4 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx @@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/test'; import { graphql, HttpResponse } from 'msw'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { AppPath } from '~/modules/types/AppPath'; import { GET_CURRENT_USER } from '~/modules/users/graphql/queries/getCurrentUser'; import { InviteTeam } from '~/pages/onboarding/InviteTeam'; @@ -27,7 +27,7 @@ const meta: Meta = { data: { currentUser: { ...mockedOnboardingUsersData[0], - onboardingStep: OnboardingStep.InviteTeam, + onboardingStatus: OnboardingStatus.InviteTeam, }, }, }); diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx index d6f7ba36dd8..4ac04fd8d74 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx @@ -3,7 +3,7 @@ import { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/test'; import { graphql, HttpResponse } from 'msw'; -import { OnboardingStep } from '~/generated/graphql'; +import { OnboardingStatus } from '~/generated/graphql'; import { AppPath } from '~/modules/types/AppPath'; import { GET_CURRENT_USER } from '~/modules/users/graphql/queries/getCurrentUser'; import { SyncEmails } from '~/pages/onboarding/SyncEmails'; @@ -27,7 +27,7 @@ const meta: Meta = { data: { currentUser: { ...mockedOnboardingUsersData[0], - onboardingStep: OnboardingStep.SyncEmail, + onboardingStatus: OnboardingStatus.SyncEmail, }, }, }); diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index caf72d83f05..4d8eeb0db0f 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -25,7 +25,7 @@ import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer' import { Section } from '@/ui/layout/section/components/Section'; import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { - OnboardingStep, + OnboardingStatus, useBillingPortalSessionQuery, useUpdateBillingSubscriptionMutation, } from '~/generated/graphql'; @@ -87,21 +87,21 @@ export const SettingsBilling = () => { loading || !isDefined(data) || !isDefined(data.billingPortalSession.url); const switchIntervalButtonDisabled = - onboardingStatus !== OnboardingStep.Completed; + onboardingStatus !== OnboardingStatus.Completed; const cancelPlanButtonDisabled = billingPortalButtonDisabled || - onboardingStatus !== OnboardingStep.Completed; + onboardingStatus !== OnboardingStatus.Completed; const displayPaymentFailInfo = - onboardingStatus === OnboardingStep.SubscriptionPastDue || - onboardingStatus === OnboardingStep.SubscriptionUnpaid; + onboardingStatus === OnboardingStatus.SubscriptionPastDue || + onboardingStatus === OnboardingStatus.SubscriptionUnpaid; const displaySubscriptionCanceledInfo = - onboardingStatus === OnboardingStep.SubscriptionCanceled; + onboardingStatus === OnboardingStatus.SubscriptionCanceled; const displaySubscribeInfo = - onboardingStatus === OnboardingStep.CompletedWithoutSubscription; + onboardingStatus === OnboardingStatus.CompletedWithoutSubscription; const openBillingPortal = () => { if (isDefined(data) && isDefined(data.billingPortalSession.url)) { diff --git a/packages/twenty-front/src/testing/mock-data/users.ts b/packages/twenty-front/src/testing/mock-data/users.ts index 525919166aa..72fd5b04d78 100644 --- a/packages/twenty-front/src/testing/mock-data/users.ts +++ b/packages/twenty-front/src/testing/mock-data/users.ts @@ -10,7 +10,7 @@ type MockedUser = Pick< | 'canImpersonate' | '__typename' | 'supportUserHash' - | 'onboardingStep' + | 'onboardingStatus' > & { workspaceMember: WorkspaceMember | null; locale: string; @@ -93,7 +93,7 @@ export const mockedUsersData: Array = [ defaultWorkspace: mockDefaultWorkspace, locale: 'en', workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStep: null, + onboardingStatus: null, }, { id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6c', @@ -116,7 +116,7 @@ export const mockedUsersData: Array = [ defaultWorkspace: mockDefaultWorkspace, locale: 'en', workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStep: null, + onboardingStatus: null, }, ]; @@ -143,7 +143,7 @@ export const mockedOnboardingUsersData: Array = [ defaultWorkspace: mockDefaultWorkspace, locale: 'en', workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStep: null, + onboardingStatus: null, }, { id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d', @@ -159,6 +159,6 @@ export const mockedOnboardingUsersData: Array = [ }, locale: 'en', workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStep: null, + onboardingStatus: null, }, ]; diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts similarity index 94% rename from packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts rename to packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts index 518d1a86c2d..9fc4ac93f7e 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-step.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts @@ -1,4 +1,4 @@ -export enum OnboardingStep { +export enum OnboardingStatus { SUBSCRIPTION_INCOMPLETE = 'SUBSCRIPTION_INCOMPLETE', SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED', SUBSCRIPTION_PAST_DUE = 'SUBSCRIPTION_PAST_DUE', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index fb198f2bd4d..ed729058244 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { KeyValuePairService } from 'src/engine/core-modules/key-value-pair/key-value-pair.service'; -import { OnboardingStep } from 'src/engine/core-modules/onboarding/enums/onboarding-step.enum'; +import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum'; import { User } from 'src/engine/core-modules/user/user.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; @@ -42,7 +42,7 @@ export class OnboardingService { private readonly workspaceMemberRepository: WorkspaceMemberRepository, ) {} - private async isSyncEmailOnboardingStep(user: User) { + private async isSyncEmailOnboardingStatus(user: User) { const syncEmailValue = await this.keyValuePairService.get({ userId: user.id, workspaceId: user.defaultWorkspaceId, @@ -58,7 +58,7 @@ export class OnboardingService { return !isSyncEmailSkipped && !connectedAccounts?.length; } - private async isInviteTeamOnboardingStep(workspace: Workspace) { + private async isInviteTeamOnboardingStatus(workspace: Workspace) { const inviteTeamValue = await this.keyValuePairService.get({ workspaceId: workspace.id, key: OnboardingStepKeys.INVITE_TEAM_ONBOARDING_STEP, @@ -74,10 +74,10 @@ export class OnboardingService { ); } - async getOnboardingStep(user: User): Promise { + async getOnboardingStatus(user: User): Promise { if (this.environmentService.get('IS_BILLING_ENABLED')) { if (user.defaultWorkspace.subscriptionStatus === 'incomplete') { - return OnboardingStep.SUBSCRIPTION_INCOMPLETE; + return OnboardingStatus.SUBSCRIPTION_INCOMPLETE; } } @@ -86,7 +86,7 @@ export class OnboardingService { user.defaultWorkspaceId, )) ) { - return OnboardingStep.WORKSPACE_ACTIVATION; + return OnboardingStatus.WORKSPACE_ACTIVATION; } const workspaceMember = await this.workspaceMemberRepository.getById( @@ -98,28 +98,28 @@ export class OnboardingService { workspaceMember && (!workspaceMember.name.firstName || !workspaceMember.name.lastName) ) { - return OnboardingStep.PROFILE_CREATION; + return OnboardingStatus.PROFILE_CREATION; } - if (await this.isSyncEmailOnboardingStep(user)) { - return OnboardingStep.SYNC_EMAIL; + if (await this.isSyncEmailOnboardingStatus(user)) { + return OnboardingStatus.SYNC_EMAIL; } - if (await this.isInviteTeamOnboardingStep(user.defaultWorkspace)) { - return OnboardingStep.INVITE_TEAM; + if (await this.isInviteTeamOnboardingStatus(user.defaultWorkspace)) { + return OnboardingStatus.INVITE_TEAM; } if (this.environmentService.get('IS_BILLING_ENABLED')) { if (user.defaultWorkspace.subscriptionStatus === 'canceled') { - return OnboardingStep.SUBSCRIPTION_CANCELED; + return OnboardingStatus.SUBSCRIPTION_CANCELED; } if (user.defaultWorkspace.subscriptionStatus === 'past_due') { - return OnboardingStep.SUBSCRIPTION_PAST_DUE; + return OnboardingStatus.SUBSCRIPTION_PAST_DUE; } if (user.defaultWorkspace.subscriptionStatus === 'unpaid') { - return OnboardingStep.SUBSCRIPTION_UNPAID; + return OnboardingStatus.SUBSCRIPTION_UNPAID; } if ( @@ -127,11 +127,11 @@ export class OnboardingService { workspaceId: user.defaultWorkspaceId, })) ) { - return OnboardingStep.COMPLETED_WITHOUT_SUBSCRIPTION; + return OnboardingStatus.COMPLETED_WITHOUT_SUBSCRIPTION; } } - return OnboardingStep.COMPLETED; + return OnboardingStatus.COMPLETED; } async skipInviteTeamOnboardingStep(workspaceId: string) { diff --git a/packages/twenty-server/src/engine/core-modules/user/user.entity.ts b/packages/twenty-server/src/engine/core-modules/user/user.entity.ts index 4a855db1035..54c37148193 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.entity.ts @@ -18,11 +18,11 @@ import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-mem import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; import { KeyValuePair } from 'src/engine/core-modules/key-value-pair/key-value-pair.entity'; -import { OnboardingStep } from 'src/engine/core-modules/onboarding/enums/onboarding-step.enum'; +import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum'; -registerEnumType(OnboardingStep, { - name: 'OnboardingStep', - description: 'Onboarding step', +registerEnumType(OnboardingStatus, { + name: 'OnboardingStatus', + description: 'Onboarding status', }); @Entity({ name: 'user', schema: 'core' }) @@ -119,6 +119,6 @@ export class User { @OneToMany(() => UserWorkspace, (userWorkspace) => userWorkspace.user) workspaces: Relation; - @Field(() => OnboardingStep, { nullable: true }) - onboardingStep: OnboardingStep; + @Field(() => OnboardingStatus, { nullable: true }) + onboardingStatus: OnboardingStatus; } diff --git a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts index f8a2cf0af5c..02ca0cd720f 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts @@ -27,7 +27,7 @@ import { DemoEnvGuard } from 'src/engine/guards/demo.env.guard'; import { JwtAuthGuard } from 'src/engine/guards/jwt.auth.guard'; import { User } from 'src/engine/core-modules/user/user.entity'; import { WorkspaceMember } from 'src/engine/core-modules/user/dtos/workspace-member.dto'; -import { OnboardingStep } from 'src/engine/core-modules/onboarding/enums/onboarding-step.enum'; +import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; const getHMACKey = (email?: string, key?: string | null) => { @@ -116,12 +116,14 @@ export class UserResolver { return this.userService.deleteUser(userId); } - @ResolveField(() => OnboardingStep) - async onboardingStep(@Parent() user: User): Promise { + @ResolveField(() => OnboardingStatus) + async onboardingStatus( + @Parent() user: User, + ): Promise { if (!user) { return null; } - return this.onboardingService.getOnboardingStep(user); + return this.onboardingService.getOnboardingStatus(user); } } From 32cfe6601dbc883168c2db638955dfef0b8d5fd8 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 13:27:42 +0200 Subject: [PATCH 10/47] Use setNextOnboardingStatus for createProfile step --- .../hooks/useSetNextOnboardingStatus.ts | 3 +++ .../src/pages/onboarding/CreateProfile.tsx | 17 +++++------------ 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index 8798a4f6095..92a9fe9477b 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -15,6 +15,9 @@ const getNextOnboardingStatus = ( workspaceMembers: WorkspaceMember[], currentWorkspace: CurrentWorkspace | null, ) => { + if (currentOnboardingStatus === OnboardingStatus.ProfileCreation) { + return OnboardingStatus.SyncEmail; + } if (currentOnboardingStatus === OnboardingStatus.SyncEmail) { return workspaceMembers && workspaceMembers.length > 1 ? null diff --git a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx index 8b6f7f42189..2698041c3db 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import styled from '@emotion/styled'; import { zodResolver } from '@hookform/resolvers/zod'; -import { useRecoilState, useSetRecoilState } from 'recoil'; +import { useRecoilState } from 'recoil'; import { Key } from 'ts-key-enum'; import { H2Title } from 'twenty-ui'; import { z } from 'zod'; @@ -10,10 +10,10 @@ import { z } from 'zod'; import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; -import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; +import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus'; import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; @@ -56,12 +56,11 @@ type Form = z.infer; export const CreateProfile = () => { const onboardingStatus = useOnboardingStatus(); + const setNextOnboardingStatus = useSetNextOnboardingStatus(); const { enqueueSnackBar } = useSnackBar(); const [currentWorkspaceMember, setCurrentWorkspaceMember] = useRecoilState( currentWorkspaceMemberState, ); - const setCurrentUser = useSetRecoilState(currentUserState); - const { updateOneRecord } = useUpdateOneRecord({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, }); @@ -113,13 +112,7 @@ export const CreateProfile = () => { colorScheme: 'System', }) as any, ); - setCurrentUser( - (current) => - ({ - ...current, - onboardingStatus: OnboardingStatus.SyncEmail, - }) as any, - ); + setNextOnboardingStatus(OnboardingStatus.ProfileCreation); } catch (error: any) { enqueueSnackBar(error?.message, { variant: SnackBarVariant.Error, @@ -128,7 +121,7 @@ export const CreateProfile = () => { }, [ currentWorkspaceMember?.id, - setCurrentUser, + setNextOnboardingStatus, enqueueSnackBar, setCurrentWorkspaceMember, updateOneRecord, From bcc421225f2a6c04715f68afd8e4e6e15f927b88 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 13:37:15 +0200 Subject: [PATCH 11/47] Clean code --- .../onboarding/onboarding.service.ts | 114 ++++++++++++------ 1 file changed, 76 insertions(+), 38 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index ed729058244..0e44746ffc6 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -42,6 +42,31 @@ export class OnboardingService { private readonly workspaceMemberRepository: WorkspaceMemberRepository, ) {} + private async isSubscriptionIncompleteOnboardingStatus(user: User) { + return ( + this.environmentService.get('IS_BILLING_ENABLED') && + user.defaultWorkspace.subscriptionStatus === 'incomplete' + ); + } + + private async isWorkspaceActivationOnboardingStatus(user: User) { + return !(await this.workspaceManagerService.doesDataSourceExist( + user.defaultWorkspaceId, + )); + } + + private async isProfileCreationOnboardingStatus(user: User) { + const workspaceMember = await this.workspaceMemberRepository.getById( + user.id, + user.defaultWorkspaceId, + ); + + return ( + workspaceMember && + (!workspaceMember.name.firstName || !workspaceMember.name.lastName) + ); + } + private async isSyncEmailOnboardingStatus(user: User) { const syncEmailValue = await this.keyValuePairService.get({ userId: user.id, @@ -74,30 +99,49 @@ export class OnboardingService { ); } + private async isSubscriptionCanceledOnboardingStatus(user: User) { + return ( + this.environmentService.get('IS_BILLING_ENABLED') && + user.defaultWorkspace.subscriptionStatus === 'canceled' + ); + } + + private async isSubscriptionPastDueOnboardingStatus(user: User) { + return ( + this.environmentService.get('IS_BILLING_ENABLED') && + user.defaultWorkspace.subscriptionStatus === 'past_due' + ); + } + + private async isSubscriptionUnpaidOnboardingStatus(user: User) { + return ( + this.environmentService.get('IS_BILLING_ENABLED') && + user.defaultWorkspace.subscriptionStatus === 'unpaid' + ); + } + + private async isCompletedWithoutSubscriptionOnboardingStatus(user: User) { + const currentBillingSubscription = + await this.billingService.getCurrentBillingSubscription({ + workspaceId: user.defaultWorkspaceId, + }); + + return ( + this.environmentService.get('IS_BILLING_ENABLED') && + !currentBillingSubscription + ); + } + async getOnboardingStatus(user: User): Promise { - if (this.environmentService.get('IS_BILLING_ENABLED')) { - if (user.defaultWorkspace.subscriptionStatus === 'incomplete') { - return OnboardingStatus.SUBSCRIPTION_INCOMPLETE; - } + if (await this.isSubscriptionIncompleteOnboardingStatus(user)) { + return OnboardingStatus.SUBSCRIPTION_INCOMPLETE; } - if ( - !(await this.workspaceManagerService.doesDataSourceExist( - user.defaultWorkspaceId, - )) - ) { + if (await this.isWorkspaceActivationOnboardingStatus(user)) { return OnboardingStatus.WORKSPACE_ACTIVATION; } - const workspaceMember = await this.workspaceMemberRepository.getById( - user.id, - user.defaultWorkspaceId, - ); - - if ( - workspaceMember && - (!workspaceMember.name.firstName || !workspaceMember.name.lastName) - ) { + if (await this.isProfileCreationOnboardingStatus(user)) { return OnboardingStatus.PROFILE_CREATION; } @@ -109,26 +153,20 @@ export class OnboardingService { return OnboardingStatus.INVITE_TEAM; } - if (this.environmentService.get('IS_BILLING_ENABLED')) { - if (user.defaultWorkspace.subscriptionStatus === 'canceled') { - return OnboardingStatus.SUBSCRIPTION_CANCELED; - } - - if (user.defaultWorkspace.subscriptionStatus === 'past_due') { - return OnboardingStatus.SUBSCRIPTION_PAST_DUE; - } - - if (user.defaultWorkspace.subscriptionStatus === 'unpaid') { - return OnboardingStatus.SUBSCRIPTION_UNPAID; - } - - if ( - !(await this.billingService.getCurrentBillingSubscription({ - workspaceId: user.defaultWorkspaceId, - })) - ) { - return OnboardingStatus.COMPLETED_WITHOUT_SUBSCRIPTION; - } + if (await this.isSubscriptionCanceledOnboardingStatus(user)) { + return OnboardingStatus.SUBSCRIPTION_CANCELED; + } + + if (await this.isSubscriptionPastDueOnboardingStatus(user)) { + return OnboardingStatus.SUBSCRIPTION_PAST_DUE; + } + + if (await this.isSubscriptionUnpaidOnboardingStatus(user)) { + return OnboardingStatus.SUBSCRIPTION_UNPAID; + } + + if (await this.isCompletedWithoutSubscriptionOnboardingStatus(user)) { + return OnboardingStatus.COMPLETED_WITHOUT_SUBSCRIPTION; } return OnboardingStatus.COMPLETED; From 6e35bc803d36ec67f25738ee3fa2e48c8b3b36b2 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 13:53:05 +0200 Subject: [PATCH 12/47] Use USER_CREATION status --- .../modules/auth/hooks/useOnboardingStatus.ts | 10 +- .../__test__/getOnboardingStatus.test.ts | 103 ------------------ .../modules/auth/utils/getOnboardingStatus.ts | 22 ---- .../enums/onboarding-status.enum.ts | 2 +- .../onboarding/onboarding.service.ts | 6 +- .../engine/core-modules/user/user.resolver.ts | 8 +- 6 files changed, 8 insertions(+), 143 deletions(-) delete mode 100644 packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts delete mode 100644 packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts index 6c9d515731a..8e28b0575de 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts @@ -3,15 +3,7 @@ import { useRecoilValue } from 'recoil'; import { currentUserState } from '@/auth/states/currentUserState'; import { OnboardingStatus } from '~/generated/graphql'; -import { useIsLogged } from '../hooks/useIsLogged'; -import { getOnboardingStatus } from '../utils/getOnboardingStatus'; - export const useOnboardingStatus = (): OnboardingStatus | null | undefined => { const currentUser = useRecoilValue(currentUserState); - const isLoggedIn = useIsLogged(); - - return getOnboardingStatus({ - isLoggedIn, - currentUser, - }); + return currentUser?.onboardingStatus || OnboardingStatus.UserCreation; }; diff --git a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts deleted file mode 100644 index 4993c5f266b..00000000000 --- a/packages/twenty-front/src/modules/auth/utils/__test__/getOnboardingStatus.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { CurrentUser } from '@/auth/states/currentUserState'; -import { OnboardingStatus } from '~/generated/graphql'; - -import { getOnboardingStatus } from '../getOnboardingStatus'; - -describe('getOnboardingStatus', () => { - it('should return the correct status', () => { - const ongoingUserCreation = getOnboardingStatus({ - isLoggedIn: false, - currentUser: null, - }); - - const ongoingWorkspaceActivation = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.WorkspaceActivation, - } as CurrentUser, - }); - - const ongoingProfileCreation = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.ProfileCreation, - } as CurrentUser, - }); - - const ongoingSyncEmail = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.SyncEmail, - } as CurrentUser, - }); - - const ongoingInviteTeam = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.InviteTeam, - } as CurrentUser, - }); - - const completed = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: null, - } as CurrentUser, - }); - - const incomplete = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.SubscriptionIncomplete, - } as CurrentUser, - }); - - const incompleteButBillingDisabled = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: null, - } as CurrentUser, - }); - - const canceled = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.SubscriptionCanceled, - } as CurrentUser, - }); - - const past_due = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.SubscriptionPastDue, - } as CurrentUser, - }); - - const unpaid = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.SubscriptionUnpaid, - } as CurrentUser, - }); - - const completeWithoutSubscription = getOnboardingStatus({ - isLoggedIn: true, - currentUser: { - onboardingStatus: OnboardingStatus.CompletedWithoutSubscription, - } as CurrentUser, - }); - - expect(ongoingUserCreation).toBe('ongoing_user_creation'); - expect(ongoingWorkspaceActivation).toBe('ongoing_workspace_activation'); - expect(ongoingProfileCreation).toBe('ongoing_profile_creation'); - expect(ongoingSyncEmail).toBe('ongoing_sync_email'); - expect(ongoingInviteTeam).toBe('ongoing_invite_team'); - expect(completed).toBe('completed'); - expect(incomplete).toBe('incomplete'); - expect(canceled).toBe('canceled'); - expect(past_due).toBe('past_due'); - expect(unpaid).toBe('unpaid'); - expect(incompleteButBillingDisabled).toBe('completed'); - expect(completeWithoutSubscription).toBe('completed_without_subscription'); - }); -}); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts deleted file mode 100644 index d58e5f7651c..00000000000 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { CurrentUser } from '@/auth/states/currentUserState'; -import { OnboardingStatus } from '~/generated/graphql'; - -export const getOnboardingStatus = ({ - isLoggedIn, - currentUser, -}: { - isLoggedIn: boolean; - currentUser: CurrentUser | null; -}) => { - if (!isLoggedIn) { - return OnboardingStatus.UserCreation; - } - - // After SignInUp, the user should have a current workspace assigned. - // If not, it indicates that the data is still being requested. - if (!currentUser) { - return undefined; - } - - return currentUser.onboardingStatus; -}; diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts index 9fc4ac93f7e..6e7311c4a47 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts @@ -3,7 +3,7 @@ export enum OnboardingStatus { SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED', SUBSCRIPTION_PAST_DUE = 'SUBSCRIPTION_PAST_DUE', SUBSCRIPTION_UNPAID = 'SUBSCRIPTION_UNPAID', - USER_CREATION = 'USER_CREATION', //Don't remove as used in front graphql generated file + USER_CREATION = 'USER_CREATION', WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 0e44746ffc6..153538d7752 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -132,7 +132,11 @@ export class OnboardingService { ); } - async getOnboardingStatus(user: User): Promise { + async getOnboardingStatus(user?: User) { + if (!user) { + return OnboardingStatus.USER_CREATION; + } + if (await this.isSubscriptionIncompleteOnboardingStatus(user)) { return OnboardingStatus.SUBSCRIPTION_INCOMPLETE; } diff --git a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts index 02ca0cd720f..19d63e5814e 100644 --- a/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/user/user.resolver.ts @@ -117,13 +117,7 @@ export class UserResolver { } @ResolveField(() => OnboardingStatus) - async onboardingStatus( - @Parent() user: User, - ): Promise { - if (!user) { - return null; - } - + async onboardingStatus(@Parent() user: User): Promise { return this.onboardingService.getOnboardingStatus(user); } } From ab23d296e1261be69325bdc055dd0e6e68d455ee Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 15:21:23 +0200 Subject: [PATCH 13/47] Fix redirections --- ...sePageChangeEffectNavigateLocation.test.ts | 18 ++++++++--------- .../usePageChangeEffectNavigateLocation.ts | 20 ++++++++++++------- .../modules/auth/hooks/useOnboardingStatus.ts | 7 ++++++- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 0d15a55dc15..4666a97a725 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -31,7 +31,7 @@ const testCases = [ { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.Verify, status: OnboardingStatus.UserCreation, res: undefined }, { loc: AppPath.Verify, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.Verify, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, @@ -43,7 +43,7 @@ const testCases = [ { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.SignInUp, status: OnboardingStatus.UserCreation, res: undefined }, { loc: AppPath.SignInUp, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.SignInUp, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, @@ -79,7 +79,7 @@ const testCases = [ { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.WorkspaceActivation, res: undefined }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, @@ -91,7 +91,7 @@ const testCases = [ { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.CreateProfile, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, { loc: AppPath.CreateProfile, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.CreateProfile, status: OnboardingStatus.ProfileCreation, res: undefined }, @@ -103,7 +103,7 @@ const testCases = [ { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.SyncEmails, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, { loc: AppPath.SyncEmails, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.SyncEmails, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, @@ -115,7 +115,7 @@ const testCases = [ { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.InviteTeam, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, { loc: AppPath.InviteTeam, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.InviteTeam, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, @@ -126,8 +126,8 @@ const testCases = [ { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionIncomplete, res: undefined }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.PlanRequired, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, { loc: AppPath.PlanRequired, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.PlanRequired, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, @@ -139,7 +139,7 @@ const testCases = [ { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.WorkspaceActivation, res: undefined }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index b0d5719da32..5f1a2f6b7f4 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -4,7 +4,6 @@ import { SettingsPath } from '@/types/SettingsPath'; import { OnboardingStatus } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; -import { isDefined } from '~/utils/isDefined'; export const usePageChangeEffectNavigateLocation = () => { const isMatchingLocation = useIsMatchingLocation(); @@ -48,11 +47,17 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - isDefined(onboardingStatus) && - [ - OnboardingStatus.SubscriptionUnpaid, - OnboardingStatus.SubscriptionCanceled, - ].includes(onboardingStatus) && + onboardingStatus === OnboardingStatus.SubscriptionUnpaid && + !isMatchingLocation(AppPath.SettingsCatchAll) + ) { + return `${AppPath.SettingsCatchAll.replace('/*', '')}/${ + SettingsPath.Billing + }`; + } + + if ( + (onboardingStatus === OnboardingStatus.SubscriptionUnpaid || + onboardingStatus === OnboardingStatus.SubscriptionCanceled) && !( isMatchingLocation(AppPath.SettingsCatchAll) || isMatchingLocation(AppPath.PlanRequired) @@ -93,7 +98,8 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStatus.Completed && + (onboardingStatus === OnboardingStatus.Completed || + onboardingStatus === OnboardingStatus.SubscriptionPastDue) && isMatchingOnboardingRoute ) { return defaultHomePagePath; diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts index 8e28b0575de..8c7edea3a16 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts @@ -1,9 +1,14 @@ import { useRecoilValue } from 'recoil'; +import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { currentUserState } from '@/auth/states/currentUserState'; import { OnboardingStatus } from '~/generated/graphql'; export const useOnboardingStatus = (): OnboardingStatus | null | undefined => { const currentUser = useRecoilValue(currentUserState); - return currentUser?.onboardingStatus || OnboardingStatus.UserCreation; + const isLoggedIn = useIsLogged(); + if (!isLoggedIn) { + return OnboardingStatus.UserCreation; + } + return currentUser?.onboardingStatus; }; From 29bc0bb2e83b9c2c4de7bc4f3c3d8b5fdeb842a7 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 15:53:12 +0200 Subject: [PATCH 14/47] Fix tests --- .../__test__/useOnboardingStatus.test.ts | 227 ++---------------- 1 file changed, 20 insertions(+), 207 deletions(-) diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index d804d7f8734..a617a92fb2d 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -4,11 +4,6 @@ import { RecoilRoot, useSetRecoilState } from 'recoil'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { CurrentUser, currentUserState } from '@/auth/states/currentUserState'; -import { - CurrentWorkspace, - currentWorkspaceState, -} from '@/auth/states/currentWorkspaceState'; -import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState'; import { tokenPairState } from '@/auth/states/tokenPairState'; import { OnboardingStatus } from '~/generated/graphql'; @@ -16,41 +11,22 @@ const tokenPair = { accessToken: { token: 'accessToken', expiresAt: 'expiresAt' }, refreshToken: { token: 'refreshToken', expiresAt: 'expiresAt' }, }; -const billing = { - billingUrl: 'testing.com', - isBillingEnabled: true, -}; const currentUser = { id: '1', - email: 'test@test', - supportUserHash: '1', - canImpersonate: false, onboardingStatus: null, } as CurrentUser; -const currentWorkspace = { - activationStatus: 'active', - id: '1', - allowImpersonation: true, - currentBillingSubscription: { - status: 'trialing', - }, -} as CurrentWorkspace; const renderHooks = () => { const { result } = renderHook( () => { const onboardingStatus = useOnboardingStatus(); - const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); const setCurrentUser = useSetRecoilState(currentUserState); const setTokenPair = useSetRecoilState(tokenPairState); - const setVerifyPending = useSetRecoilState(isVerifyPendingState); return { onboardingStatus, setCurrentUser, - setCurrentWorkspace, setTokenPair, - setVerifyPending, }; }, { @@ -61,192 +37,29 @@ const renderHooks = () => { }; describe('useOnboardingStatus', () => { - it('should return "ongoing_user_creation" when user is not logged in', async () => { - const { result } = renderHooks(); - - expect(result.current.onboardingStatus).toBe('ongoing_user_creation'); - }); - - it('should return "incomplete"', async () => { + it(`should return "${OnboardingStatus.UserCreation}" when user is not logged in`, async () => { const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.SubscriptionIncomplete, - }); - setCurrentWorkspace(currentWorkspace); - }); - - expect(result.current.onboardingStatus).toBe('incomplete'); + expect(result.current.onboardingStatus).toBe(OnboardingStatus.UserCreation); }); - it('should return "canceled"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.SubscriptionCanceled, - }); - setCurrentWorkspace(currentWorkspace); - }); - - expect(result.current.onboardingStatus).toBe('canceled'); - }); - - it('should return "ongoing_workspace_activation"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.WorkspaceActivation, + Object.values(OnboardingStatus) + .filter( + (onboardingStatus) => onboardingStatus !== OnboardingStatus.UserCreation, + ) + .forEach((onboardingStatus) => { + it(`should return "${onboardingStatus}"`, async () => { + const { result } = renderHooks(); + const { setTokenPair, setCurrentUser } = result.current; + + act(() => { + setTokenPair(tokenPair); + setCurrentUser({ + ...currentUser, + onboardingStatus, + }); + }); + + expect(result.current.onboardingStatus).toBe(onboardingStatus); }); - setCurrentWorkspace(currentWorkspace); }); - - expect(result.current.onboardingStatus).toBe( - 'ongoing_workspace_activation', - ); - }); - - it('should return "ongoing_profile_creation"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.ProfileCreation, - }); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'active', - }); - }); - - expect(result.current.onboardingStatus).toBe('ongoing_profile_creation'); - }); - - it('should return "ongoing_sync_email"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.SyncEmail, - }); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'active', - }); - }); - - expect(result.current.onboardingStatus).toBe('ongoing_sync_email'); - }); - - it('should return "ongoing_invite_team"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.InviteTeam, - }); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'active', - }); - }); - - expect(result.current.onboardingStatus).toBe('ongoing_invite_team'); - }); - - it('should return "completed"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser(currentUser); - setCurrentWorkspace({ - ...currentWorkspace, - subscriptionStatus: 'active', - }); - }); - - expect(result.current.onboardingStatus).toBe('completed'); - }); - - it('should return "past_due"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.SubscriptionPastDue, - }); - setCurrentWorkspace(currentWorkspace); - }); - - expect(result.current.onboardingStatus).toBe('past_due'); - }); - - it('should return "unpaid"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.SubscriptionUnpaid, - }); - setCurrentWorkspace(currentWorkspace); - }); - - expect(result.current.onboardingStatus).toBe('unpaid'); - }); - - it('should return "completed_without_subscription"', async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser, setCurrentWorkspace } = - result.current; - - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus: OnboardingStatus.CompletedWithoutSubscription, - }); - setCurrentWorkspace(currentWorkspace); - }); - - expect(result.current.onboardingStatus).toBe( - 'completed_without_subscription', - ); - }); }); From 3caf1a5d47748541d663e6d9354ff74e645dc7b8 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 16:32:32 +0200 Subject: [PATCH 15/47] Fix stories --- .../__stories__/ChooseYourPlan.stories.tsx | 6 +++++- .../__stories__/CreateProfile.stories.tsx | 6 +++++- .../__stories__/CreateWorkspace.stories.tsx | 17 ++++++----------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx index 7325383bfd5..f1ecf1a198d 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx @@ -5,6 +5,7 @@ import { graphql, HttpResponse } from 'msw'; import { AppPath } from '@/types/AppPath'; import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; +import { OnboardingStatus } from '~/generated/graphql'; import { ChooseYourPlan } from '~/pages/onboarding/ChooseYourPlan'; import { PageDecorator, @@ -25,7 +26,10 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: mockedOnboardingUsersData[0], + currentUser: { + ...mockedOnboardingUsersData[0], + onboardingStatus: OnboardingStatus.SubscriptionIncomplete, + }, }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx index 026ef4c6033..156ae4fdd05 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx @@ -5,6 +5,7 @@ import { graphql, HttpResponse } from 'msw'; import { AppPath } from '@/types/AppPath'; import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; +import { OnboardingStatus } from '~/generated/graphql'; import { CreateProfile } from '~/pages/onboarding/CreateProfile'; import { PageDecorator, @@ -24,7 +25,10 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: mockedOnboardingUsersData[0], + currentUser: { + ...mockedOnboardingUsersData[0], + onboardingStatus: OnboardingStatus.ProfileCreation, + }, }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx index f881ef40ab3..33e96539731 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx @@ -2,11 +2,10 @@ import { getOperationName } from '@apollo/client/utilities'; import { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/test'; import { graphql, HttpResponse } from 'msw'; -import { useSetRecoilState } from 'recoil'; -import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { AppPath } from '@/types/AppPath'; import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; +import { OnboardingStatus } from '~/generated/graphql'; import { CreateWorkspace } from '~/pages/onboarding/CreateWorkspace'; import { PageDecorator, @@ -18,14 +17,7 @@ import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; const meta: Meta = { title: 'Pages/Onboarding/CreateWorkspace', component: CreateWorkspace, - decorators: [ - (Story) => { - const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); - setCurrentWorkspace(mockedOnboardingUsersData[1].defaultWorkspace); - return ; - }, - PageDecorator, - ], + decorators: [PageDecorator], args: { routePath: AppPath.CreateWorkspace }, parameters: { msw: { @@ -33,7 +25,10 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: mockedOnboardingUsersData[1], + currentUser: { + ...mockedOnboardingUsersData[0], + onboardingStatus: OnboardingStatus.WorkspaceActivation, + }, }, }); }), From 86afc6b191ffc4708514404dd56c8976e88c1e9f Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 16:45:00 +0200 Subject: [PATCH 16/47] Update onboarding mocked data --- .../src/__stories__/App.stories.tsx | 6 +- .../__tests__/useDefaultHomePagePath.test.ts | 4 +- .../UserOrMetadataLoader.stories.tsx | 4 +- .../__stories__/SupportChat.stories.tsx | 4 +- .../__stories__/PasswordReset.stories.tsx | 17 +++- .../__stories__/ChooseYourPlan.stories.tsx | 9 +- .../__stories__/CreateProfile.stories.tsx | 9 +- .../__stories__/CreateWorkspace.stories.tsx | 9 +- .../__stories__/InviteTeam.stories.tsx | 9 +- .../__stories__/PaymentSuccess.stories.tsx | 7 +- .../__stories__/SyncEmails.stories.tsx | 7 +- .../ObjectMetadataItemsDecorator.tsx | 4 +- .../twenty-front/src/testing/graphqlMocks.ts | 4 +- .../src/testing/mock-data/users.ts | 96 +++++-------------- 14 files changed, 72 insertions(+), 117 deletions(-) diff --git a/packages/twenty-front/src/__stories__/App.stories.tsx b/packages/twenty-front/src/__stories__/App.stories.tsx index 761c64f3bb8..c5314b5652a 100644 --- a/packages/twenty-front/src/__stories__/App.stories.tsx +++ b/packages/twenty-front/src/__stories__/App.stories.tsx @@ -13,7 +13,7 @@ import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/Sn import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; import { App } from '~/App'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedUsersData } from '~/testing/mock-data/users'; +import { mockedUserData } from '~/testing/mock-data/users'; const meta: Meta = { title: 'App/App', @@ -67,9 +67,9 @@ export const DarkMode: Story = { return HttpResponse.json({ data: { currentUser: { - ...mockedUsersData[0], + ...mockedUserData, workspaceMember: { - ...mockedUsersData[0].workspaceMember, + ...mockedUserData.workspaceMember, colorScheme: 'Dark', }, }, diff --git a/packages/twenty-front/src/hooks/__tests__/useDefaultHomePagePath.test.ts b/packages/twenty-front/src/hooks/__tests__/useDefaultHomePagePath.test.ts index 1d2834937e3..709c071cce7 100644 --- a/packages/twenty-front/src/hooks/__tests__/useDefaultHomePagePath.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/useDefaultHomePagePath.test.ts @@ -7,7 +7,7 @@ import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMet import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { AppPath } from '@/types/AppPath'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; -import { mockedUsersData } from '~/testing/mock-data/users'; +import { mockedUserData } from '~/testing/mock-data/users'; const objectMetadataItem = getObjectMetadataItemsMock()[0]; jest.mock('@/object-metadata/hooks/useObjectMetadataItem'); @@ -36,7 +36,7 @@ const renderHooks = (withCurrentUser: boolean) => { () => { const setCurrentUser = useSetRecoilState(currentUserState); if (withCurrentUser) { - setCurrentUser(mockedUsersData[0]); + setCurrentUser(mockedUserData); } return useDefaultHomePagePath(); }, diff --git a/packages/twenty-front/src/loading/components/__stories__/UserOrMetadataLoader.stories.tsx b/packages/twenty-front/src/loading/components/__stories__/UserOrMetadataLoader.stories.tsx index 15c18390f69..ac5a371869b 100644 --- a/packages/twenty-front/src/loading/components/__stories__/UserOrMetadataLoader.stories.tsx +++ b/packages/twenty-front/src/loading/components/__stories__/UserOrMetadataLoader.stories.tsx @@ -14,7 +14,7 @@ import { } from '~/testing/decorators/PageDecorator'; import { graphqlMocks, metadataGraphql } from '~/testing/graphqlMocks'; import { mockedClientConfig } from '~/testing/mock-data/config'; -import { mockedUsersData } from '~/testing/mock-data/users'; +import { mockedUserData } from '~/testing/mock-data/users'; const userMetadataLoaderMocks = { msw: { @@ -22,7 +22,7 @@ const userMetadataLoaderMocks = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: mockedUsersData[0], + currentUser: mockedUserData, }, }); }), diff --git a/packages/twenty-front/src/modules/support/components/__stories__/SupportChat.stories.tsx b/packages/twenty-front/src/modules/support/components/__stories__/SupportChat.stories.tsx index 7da02230650..da0c12334d1 100644 --- a/packages/twenty-front/src/modules/support/components/__stories__/SupportChat.stories.tsx +++ b/packages/twenty-front/src/modules/support/components/__stories__/SupportChat.stories.tsx @@ -10,7 +10,7 @@ import { supportChatState } from '@/client-config/states/supportChatState'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { mockDefaultWorkspace, - mockedUsersData, + mockedUserData, mockedWorkspaceMemberData, } from '~/testing/mock-data/users'; @@ -30,7 +30,7 @@ const meta: Meta = { setCurrentWorkspace(mockDefaultWorkspace); setCurrentWorkspaceMember(mockedWorkspaceMemberData); - setCurrentUser(mockedUsersData[0]); + setCurrentUser(mockedUserData); setSupportChat({ supportDriver: 'front', supportFrontChatId: '1234' }); return ; diff --git a/packages/twenty-front/src/pages/auth/__stories__/PasswordReset.stories.tsx b/packages/twenty-front/src/pages/auth/__stories__/PasswordReset.stories.tsx index 234b06e795d..ea98157a1e4 100644 --- a/packages/twenty-front/src/pages/auth/__stories__/PasswordReset.stories.tsx +++ b/packages/twenty-front/src/pages/auth/__stories__/PasswordReset.stories.tsx @@ -4,14 +4,21 @@ import { within } from '@storybook/test'; import { graphql, HttpResponse } from 'msw'; import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; -import { ValidatePasswordResetTokenDocument } from '~/generated/graphql'; +import { + OnboardingStatus, + ValidatePasswordResetTokenDocument, +} from '~/generated/graphql'; import { PasswordReset } from '~/pages/auth/PasswordReset'; import { PageDecorator, PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; +import { mockedOnboardingUserData } from '~/testing/mock-data/users'; + +const mockedOnboardingUsersData = mockedOnboardingUserData( + OnboardingStatus.Completed, +); const meta: Meta = { title: 'Pages/Auth/PasswordReset', @@ -30,8 +37,8 @@ const meta: Meta = { return HttpResponse.json({ data: { validatePasswordResetToken: { - id: mockedOnboardingUsersData[0].id, - email: mockedOnboardingUsersData[0].email, + id: mockedOnboardingUsersData.id, + email: mockedOnboardingUsersData.email, }, }, }); @@ -40,7 +47,7 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: mockedOnboardingUsersData[0], + currentUser: mockedOnboardingUsersData, }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx index f1ecf1a198d..3370984ddfc 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx @@ -12,7 +12,7 @@ import { PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; +import { mockedOnboardingUserData } from '~/testing/mock-data/users'; import { sleep } from '~/utils/sleep'; const meta: Meta = { @@ -26,10 +26,9 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: { - ...mockedOnboardingUsersData[0], - onboardingStatus: OnboardingStatus.SubscriptionIncomplete, - }, + currentUser: mockedOnboardingUserData( + OnboardingStatus.SubscriptionIncomplete, + ), }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx index 156ae4fdd05..19512ebe183 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/CreateProfile.stories.tsx @@ -12,7 +12,7 @@ import { PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; +import { mockedOnboardingUserData } from '~/testing/mock-data/users'; const meta: Meta = { title: 'Pages/Onboarding/CreateProfile', @@ -25,10 +25,9 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: { - ...mockedOnboardingUsersData[0], - onboardingStatus: OnboardingStatus.ProfileCreation, - }, + currentUser: mockedOnboardingUserData( + OnboardingStatus.ProfileCreation, + ), }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx index 33e96539731..baccc13b2c8 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/CreateWorkspace.stories.tsx @@ -12,7 +12,7 @@ import { PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; +import { mockedOnboardingUserData } from '~/testing/mock-data/users'; const meta: Meta = { title: 'Pages/Onboarding/CreateWorkspace', @@ -25,10 +25,9 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: { - ...mockedOnboardingUsersData[0], - onboardingStatus: OnboardingStatus.WorkspaceActivation, - }, + currentUser: mockedOnboardingUserData( + OnboardingStatus.WorkspaceActivation, + ), }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx index 850da79c5e4..e174fc51707 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/InviteTeam.stories.tsx @@ -12,7 +12,7 @@ import { PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; +import { mockedOnboardingUserData } from '~/testing/mock-data/users'; const meta: Meta = { title: 'Pages/Onboarding/InviteTeam', @@ -25,10 +25,9 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: { - ...mockedOnboardingUsersData[0], - onboardingStatus: OnboardingStatus.InviteTeam, - }, + currentUser: mockedOnboardingUserData( + OnboardingStatus.InviteTeam, + ), }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/PaymentSuccess.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/PaymentSuccess.stories.tsx index 79ad41ee12a..fccb1e3c64e 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/PaymentSuccess.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/PaymentSuccess.stories.tsx @@ -5,13 +5,14 @@ import { graphql, HttpResponse } from 'msw'; import { AppPath } from '@/types/AppPath'; import { GET_CURRENT_USER } from '@/users/graphql/queries/getCurrentUser'; +import { OnboardingStatus } from '~/generated/graphql'; import { PaymentSuccess } from '~/pages/onboarding/PaymentSuccess'; import { PageDecorator, PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; +import { mockedOnboardingUserData } from '~/testing/mock-data/users'; const meta: Meta = { title: 'Pages/Onboarding/PaymentSuccess', @@ -24,7 +25,9 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: mockedOnboardingUsersData[0], + currentUser: mockedOnboardingUserData( + OnboardingStatus.WorkspaceActivation, + ), }, }); }), diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx index 4ac04fd8d74..8d9b4261ef0 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/SyncEmails.stories.tsx @@ -12,7 +12,7 @@ import { PageDecoratorArgs, } from '~/testing/decorators/PageDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { mockedOnboardingUsersData } from '~/testing/mock-data/users'; +import { mockedOnboardingUserData } from '~/testing/mock-data/users'; const meta: Meta = { title: 'Pages/Onboarding/SyncEmails', @@ -25,10 +25,7 @@ const meta: Meta = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: { - ...mockedOnboardingUsersData[0], - onboardingStatus: OnboardingStatus.SyncEmail, - }, + currentUser: mockedOnboardingUserData(OnboardingStatus.SyncEmail), }, }); }), diff --git a/packages/twenty-front/src/testing/decorators/ObjectMetadataItemsDecorator.tsx b/packages/twenty-front/src/testing/decorators/ObjectMetadataItemsDecorator.tsx index 9c27a82e354..1bf08a190ff 100644 --- a/packages/twenty-front/src/testing/decorators/ObjectMetadataItemsDecorator.tsx +++ b/packages/twenty-front/src/testing/decorators/ObjectMetadataItemsDecorator.tsx @@ -8,7 +8,7 @@ import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/Obje import { PreComputedChipGeneratorsContext } from '@/object-metadata/context/PreComputedChipGeneratorsContext'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { getRecordChipGeneratorPerObjectPerField } from '@/object-record/utils/getRecordChipGeneratorPerObjectPerField'; -import { mockedUsersData } from '~/testing/mock-data/users'; +import { mockedUserData } from '~/testing/mock-data/users'; import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members'; export const ObjectMetadataItemsDecorator: Decorator = (Story) => { @@ -20,7 +20,7 @@ export const ObjectMetadataItemsDecorator: Decorator = (Story) => { useEffect(() => { setCurrentWorkspaceMember(mockWorkspaceMembers[0]); - setCurrentUser(mockedUsersData[0]); + setCurrentUser(mockedUserData); }, [setCurrentUser, setCurrentWorkspaceMember]); const chipGeneratorPerObjectPerField = useMemo(() => { diff --git a/packages/twenty-front/src/testing/graphqlMocks.ts b/packages/twenty-front/src/testing/graphqlMocks.ts index c5506183182..e1de4af9f6b 100644 --- a/packages/twenty-front/src/testing/graphqlMocks.ts +++ b/packages/twenty-front/src/testing/graphqlMocks.ts @@ -15,7 +15,7 @@ import { mockedClientConfig } from '~/testing/mock-data/config'; import { mockedObjectMetadataItemsQueryResult } from '~/testing/mock-data/metadata'; import { getPeopleMock } from '~/testing/mock-data/people'; import { mockedRemoteTables } from '~/testing/mock-data/remote-tables'; -import { mockedUsersData } from '~/testing/mock-data/users'; +import { mockedUserData } from '~/testing/mock-data/users'; import { mockedViewsData } from '~/testing/mock-data/views'; import { mockWorkspaceMembers } from '~/testing/mock-data/workspace-members'; @@ -35,7 +35,7 @@ export const graphqlMocks = { graphql.query(getOperationName(GET_CURRENT_USER) ?? '', () => { return HttpResponse.json({ data: { - currentUser: mockedUsersData[0], + currentUser: mockedUserData, }, }); }), diff --git a/packages/twenty-front/src/testing/mock-data/users.ts b/packages/twenty-front/src/testing/mock-data/users.ts index 72fd5b04d78..9690d960628 100644 --- a/packages/twenty-front/src/testing/mock-data/users.ts +++ b/packages/twenty-front/src/testing/mock-data/users.ts @@ -1,5 +1,5 @@ import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; -import { User, Workspace } from '~/generated/graphql'; +import { OnboardingStatus, User, Workspace } from '~/generated/graphql'; type MockedUser = Pick< User, @@ -79,49 +79,26 @@ export const mockedWorkspaceMemberData: WorkspaceMember = { userEmail: 'charles@test.com', }; -export const mockedUsersData: Array = [ - { - id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d', - __typename: 'User', - email: 'charles@test.com', - firstName: 'Charles', - lastName: 'Test', - canImpersonate: false, - supportUserHash: - 'a95afad9ff6f0b364e2a3fd3e246a1a852c22b6e55a3ca33745a86c201f9c10d', - workspaceMember: mockedWorkspaceMemberData, - defaultWorkspace: mockDefaultWorkspace, - locale: 'en', - workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStatus: null, - }, - { - id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6c', - __typename: 'User', - email: 'felix@test.com', - firstName: 'Felix', - lastName: 'Test', - canImpersonate: false, - supportUserHash: - '54ac3986035961724cdb9a7a30c70e6463a4b68f0ecd2014c727171a82144b74', - workspaceMember: { - ...mockedWorkspaceMemberData, - id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6c', - name: { - firstName: 'Felix', - lastName: 'Test', - }, - userId: '81aeb270-d689-4515-bd5d-35dbe956da3b', - }, - defaultWorkspace: mockDefaultWorkspace, - locale: 'en', - workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStatus: null, - }, -]; +export const mockedUserData: MockedUser = { + id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d', + __typename: 'User', + email: 'charles@test.com', + firstName: 'Charles', + lastName: 'Test', + canImpersonate: false, + supportUserHash: + 'a95afad9ff6f0b364e2a3fd3e246a1a852c22b6e55a3ca33745a86c201f9c10d', + workspaceMember: mockedWorkspaceMemberData, + defaultWorkspace: mockDefaultWorkspace, + locale: 'en', + workspaces: [{ workspace: mockDefaultWorkspace }], + onboardingStatus: OnboardingStatus.Completed, +}; -export const mockedOnboardingUsersData: Array = [ - { +export const mockedOnboardingUserData = ( + onboardingStatus?: OnboardingStatus, +) => { + return { id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d', __typename: 'User', email: 'workspace-onboarding@test.com', @@ -130,35 +107,10 @@ export const mockedOnboardingUsersData: Array = [ canImpersonate: false, supportUserHash: '4fb61d34ed3a4aeda2476d4b308b5162db9e1809b2b8277e6fdc6efc4a609254', - workspaceMember: { - ...mockedWorkspaceMemberData, - id: 'd454f075-c72f-4ebe-bac7-d28e75e74a23', - name: { - firstName: '', - lastName: '', - }, - - userId: '7f793378-b939-43b7-8642-292c9510754c', - }, - defaultWorkspace: mockDefaultWorkspace, - locale: 'en', - workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStatus: null, - }, - { - id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6d', - __typename: 'User', - email: 'profile-onboarding@test.com', - firstName: '', - lastName: '', - canImpersonate: false, workspaceMember: null, - defaultWorkspace: { - ...mockDefaultWorkspace, - activationStatus: 'inactive', - }, + defaultWorkspace: mockDefaultWorkspace, locale: 'en', workspaces: [{ workspace: mockDefaultWorkspace }], - onboardingStatus: null, - }, -]; + onboardingStatus: onboardingStatus || null, + }; +}; From a5a32750bb3d6b112e129ba84694871e37f56c08 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 19 Jun 2024 18:39:12 +0200 Subject: [PATCH 17/47] Code cleaning --- .../ui/layout/hooks/useShowAuthModal.ts | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index 03cf5dfac99..f7f32a1c3a3 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -6,7 +6,6 @@ import { AppPath } from '@/types/AppPath'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; import { OnboardingStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; -import { isDefined } from '~/utils/isDefined'; export const useShowAuthModal = () => { const isMatchingLocation = useIsMatchingLocation(); @@ -25,25 +24,19 @@ export const useShowAuthModal = () => { return isDefaultLayoutAuthModalVisible; } if ( - isDefined(onboardingStatus) && - [ - OnboardingStatus.SubscriptionIncomplete, - OnboardingStatus.UserCreation, - OnboardingStatus.ProfileCreation, - OnboardingStatus.WorkspaceActivation, - OnboardingStatus.SyncEmail, - OnboardingStatus.InviteTeam, - ].includes(onboardingStatus) + onboardingStatus === OnboardingStatus.SubscriptionIncomplete || + onboardingStatus === OnboardingStatus.UserCreation || + onboardingStatus === OnboardingStatus.ProfileCreation || + onboardingStatus === OnboardingStatus.WorkspaceActivation || + onboardingStatus === OnboardingStatus.SyncEmail || + onboardingStatus === OnboardingStatus.InviteTeam ) { return true; } if (isMatchingLocation(AppPath.PlanRequired)) { return ( - isDefined(onboardingStatus) && - [ - OnboardingStatus.CompletedWithoutSubscription, - OnboardingStatus.SubscriptionCanceled, - ].includes(onboardingStatus) + onboardingStatus === OnboardingStatus.CompletedWithoutSubscription || + onboardingStatus === OnboardingStatus.SubscriptionCanceled ); } return false; From 656f8030561991fe77bf71fe2ceec88a34ecfc6e Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 10:41:16 +0200 Subject: [PATCH 18/47] Code review: add test --- .../useSetNextOnboardingStatus.test.ts | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts diff --git a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts new file mode 100644 index 00000000000..6c8f6bdc7e0 --- /dev/null +++ b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts @@ -0,0 +1,72 @@ +import { renderHook } from '@testing-library/react'; +import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil'; +import { v4 } from 'uuid'; + +import { currentUserState } from '@/auth/states/currentUserState'; +import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus'; +import { OnboardingStatus } from '~/generated/graphql'; +import { + mockDefaultWorkspace, + mockedUserData, +} from '~/testing/mock-data/users'; + +jest.mock('@/object-record/hooks/useFindManyRecords', () => ({ + useFindManyRecords: jest.fn(), +})); + +const renderHooks = ( + onboardingStatus: OnboardingStatus, + withCurrentBillingSubscription: boolean, +) => { + const { result } = renderHook( + () => { + const useFindManyRecordsMock = jest.requireMock( + '@/object-record/hooks/useFindManyRecords', + ); + useFindManyRecordsMock.useFindManyRecords.mockReturnValue({ + records: [], + }); + const setCurrentUser = useSetRecoilState(currentUserState); + const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); + setCurrentUser({ ...mockedUserData, onboardingStatus }); + setCurrentWorkspace({ + ...mockDefaultWorkspace, + currentBillingSubscription: withCurrentBillingSubscription + ? { id: v4(), status: 'status' } + : undefined, + }); + const setNextOnboardingStatus = useSetNextOnboardingStatus(); + setNextOnboardingStatus(onboardingStatus); + return useRecoilValue(currentUserState)?.onboardingStatus; + }, + { + wrapper: RecoilRoot, + }, + ); + return result; +}; + +describe('useSetNextOnboardingStatus', () => { + it('should set next onboarding status for ProfileCreation', () => { + const result = renderHooks(OnboardingStatus.ProfileCreation, false); + expect(result.current).toEqual(OnboardingStatus.SyncEmail); + }); + + it('should set next onboarding status for SyncEmail', () => { + const result = renderHooks(OnboardingStatus.SyncEmail, false); + expect(result.current).toEqual(OnboardingStatus.InviteTeam); + }); + + it('should set next onboarding status for Completed', () => { + const result = renderHooks(OnboardingStatus.InviteTeam, true); + expect(result.current).toEqual(OnboardingStatus.Completed); + }); + + it('should set next onboarding status for Completed without subscription', () => { + const result = renderHooks(OnboardingStatus.InviteTeam, false); + expect(result.current).toEqual( + OnboardingStatus.CompletedWithoutSubscription, + ); + }); +}); From 3a82863467e68fd45b2ab9f2ea97458ddf27827c Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 12:39:12 +0200 Subject: [PATCH 19/47] Code review: use currentUserState in setNextOnboardingStatus --- .../useSetNextOnboardingStatus.test.ts | 19 ++++--- .../hooks/useSetNextOnboardingStatus.ts | 50 ++++++++++--------- .../src/pages/onboarding/CreateProfile.tsx | 2 +- .../src/pages/onboarding/InviteTeam.tsx | 2 +- .../src/pages/onboarding/SyncEmails.tsx | 2 +- 5 files changed, 42 insertions(+), 33 deletions(-) diff --git a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts index 6c8f6bdc7e0..2e4db0a4ceb 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts @@ -18,6 +18,7 @@ jest.mock('@/object-record/hooks/useFindManyRecords', () => ({ const renderHooks = ( onboardingStatus: OnboardingStatus, withCurrentBillingSubscription: boolean, + withManyWorkspaceMembers: boolean, ) => { const { result } = renderHook( () => { @@ -25,7 +26,7 @@ const renderHooks = ( '@/object-record/hooks/useFindManyRecords', ); useFindManyRecordsMock.useFindManyRecords.mockReturnValue({ - records: [], + records: withManyWorkspaceMembers ? [{}] : [], }); const setCurrentUser = useSetRecoilState(currentUserState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); @@ -36,8 +37,7 @@ const renderHooks = ( ? { id: v4(), status: 'status' } : undefined, }); - const setNextOnboardingStatus = useSetNextOnboardingStatus(); - setNextOnboardingStatus(onboardingStatus); + useSetNextOnboardingStatus()(); return useRecoilValue(currentUserState)?.onboardingStatus; }, { @@ -49,22 +49,27 @@ const renderHooks = ( describe('useSetNextOnboardingStatus', () => { it('should set next onboarding status for ProfileCreation', () => { - const result = renderHooks(OnboardingStatus.ProfileCreation, false); + const result = renderHooks(OnboardingStatus.ProfileCreation, false, false); expect(result.current).toEqual(OnboardingStatus.SyncEmail); }); it('should set next onboarding status for SyncEmail', () => { - const result = renderHooks(OnboardingStatus.SyncEmail, false); + const result = renderHooks(OnboardingStatus.SyncEmail, false, false); expect(result.current).toEqual(OnboardingStatus.InviteTeam); }); + it('should skip invite when workspaceMembers exist', () => { + const result = renderHooks(OnboardingStatus.SyncEmail, true, true); + expect(result.current).toEqual(OnboardingStatus.Completed); + }); + it('should set next onboarding status for Completed', () => { - const result = renderHooks(OnboardingStatus.InviteTeam, true); + const result = renderHooks(OnboardingStatus.InviteTeam, true, false); expect(result.current).toEqual(OnboardingStatus.Completed); }); it('should set next onboarding status for Completed without subscription', () => { - const result = renderHooks(OnboardingStatus.InviteTeam, false); + const result = renderHooks(OnboardingStatus.InviteTeam, false, false); expect(result.current).toEqual( OnboardingStatus.CompletedWithoutSubscription, ); diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index 92a9fe9477b..36fda57c2c3 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -1,6 +1,6 @@ -import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilCallback, useRecoilValue } from 'recoil'; -import { currentUserState } from '@/auth/states/currentUserState'; +import { CurrentUser, currentUserState } from '@/auth/states/currentUserState'; import { CurrentWorkspace, currentWorkspaceState, @@ -11,17 +11,18 @@ import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; import { OnboardingStatus } from '~/generated/graphql'; const getNextOnboardingStatus = ( - currentOnboardingStatus: OnboardingStatus, + currentUser: CurrentUser | null, workspaceMembers: WorkspaceMember[], currentWorkspace: CurrentWorkspace | null, ) => { - if (currentOnboardingStatus === OnboardingStatus.ProfileCreation) { + if (currentUser?.onboardingStatus === OnboardingStatus.ProfileCreation) { return OnboardingStatus.SyncEmail; } - if (currentOnboardingStatus === OnboardingStatus.SyncEmail) { - return workspaceMembers && workspaceMembers.length > 1 - ? null - : OnboardingStatus.InviteTeam; + if ( + currentUser?.onboardingStatus === OnboardingStatus.SyncEmail && + workspaceMembers?.length === 0 + ) { + return OnboardingStatus.InviteTeam; } return currentWorkspace?.currentBillingSubscription ? OnboardingStatus.Completed @@ -29,26 +30,29 @@ const getNextOnboardingStatus = ( }; export const useSetNextOnboardingStatus = () => { - const setCurrentUser = useSetRecoilState(currentUserState); const { records: workspaceMembers } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, }); const currentWorkspace = useRecoilValue(currentWorkspaceState); return useRecoilCallback( - () => (currentOnboardingStatus: OnboardingStatus) => { - setCurrentUser( - (current) => - ({ - ...current, - onboardingStatus: getNextOnboardingStatus( - currentOnboardingStatus, - workspaceMembers, - currentWorkspace, - ), - }) as any, - ); - }, - [setCurrentUser, workspaceMembers, currentWorkspace], + ({ snapshot, set }) => + () => { + const currentUser = snapshot.getLoadable(currentUserState).getValue(); + const nextOnboardingStatus = getNextOnboardingStatus( + currentUser, + workspaceMembers, + currentWorkspace, + ); + set( + currentUserState, + (current) => + ({ + ...current, + onboardingStatus: nextOnboardingStatus, + }) as any, + ); + }, + [workspaceMembers, currentWorkspace], ); }; diff --git a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx index 2698041c3db..7cfb5e0ded0 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx @@ -112,7 +112,7 @@ export const CreateProfile = () => { colorScheme: 'System', }) as any, ); - setNextOnboardingStatus(OnboardingStatus.ProfileCreation); + setNextOnboardingStatus(); } catch (error: any) { enqueueSnackBar(error?.message, { variant: SnackBarVariant.Error, diff --git a/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx b/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx index 93c13520447..53a7b212494 100644 --- a/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx +++ b/packages/twenty-front/src/pages/onboarding/InviteTeam.tsx @@ -136,7 +136,7 @@ export const InviteTeam = () => { ); const result = await sendInviteLink({ variables: { emails } }); - setNextOnboardingStatus(OnboardingStatus.InviteTeam); + setNextOnboardingStatus(); if (isDefined(result.errors)) { throw result.errors; diff --git a/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx b/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx index 9c3323bbf65..c6ddb99c82d 100644 --- a/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx +++ b/packages/twenty-front/src/pages/onboarding/SyncEmails.tsx @@ -63,7 +63,7 @@ export const SyncEmails = () => { const continueWithoutSync = async () => { await skipSyncEmailOnboardingStatusMutation(); - setNextOnboardingStatus(OnboardingStatus.SyncEmail); + setNextOnboardingStatus(); }; useScopedHotkeys( From 0b874504803d9251e020171c504d502f1fa5a850 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 15:22:29 +0200 Subject: [PATCH 20/47] Code review: remove raw query --- .../onboarding/onboarding.service.ts | 14 +++++++------- .../repositories/workspace-member.repository.ts | 16 ---------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 06704d39d17..76adf506bf8 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -9,10 +9,11 @@ import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repos import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/standard-objects/connected-account.workspace-entity'; import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; -import { WorkspaceMemberRepository } from 'src/modules/workspace-member/repositories/workspace-member.repository'; import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { BillingService } from 'src/engine/core-modules/billing/billing.service'; +import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator'; +import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; enum OnboardingStepValues { SKIPPED = 'SKIPPED', @@ -38,8 +39,8 @@ export class OnboardingService { private readonly keyValuePairService: KeyValuePairService, @InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity) private readonly connectedAccountRepository: ConnectedAccountRepository, - @InjectObjectMetadataRepository(WorkspaceMemberWorkspaceEntity) - private readonly workspaceMemberRepository: WorkspaceMemberRepository, + @InjectWorkspaceRepository(WorkspaceMemberWorkspaceEntity) + private readonly workspaceMemberRepository: WorkspaceRepository, ) {} private async isSubscriptionIncompleteOnboardingStatus(user: User) { @@ -56,10 +57,9 @@ export class OnboardingService { } private async isProfileCreationOnboardingStatus(user: User) { - const workspaceMember = await this.workspaceMemberRepository.getById( - user.id, - user.defaultWorkspaceId, - ); + const workspaceMember = await this.workspaceMemberRepository.findOneBy({ + userId: user.id, + }); return ( workspaceMember && diff --git a/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts b/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts index 02daa7b67de..aae136848e0 100644 --- a/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts +++ b/packages/twenty-server/src/modules/workspace-member/repositories/workspace-member.repository.ts @@ -14,22 +14,6 @@ export class WorkspaceMemberRepository { private readonly workspaceDataSourceService: WorkspaceDataSourceService, ) {} - public async getByIds( - userIds: string[], - workspaceId: string, - ): Promise[]> { - const dataSourceSchema = - this.workspaceDataSourceService.getSchemaName(workspaceId); - - const result = await this.workspaceDataSourceService.executeRawQuery( - `SELECT * FROM ${dataSourceSchema}."workspaceMember" WHERE "userId" = ANY($1)`, - [userIds], - workspaceId, - ); - - return result; - } - public async find(workspaceMemberId: string, workspaceId: string) { const dataSourceSchema = this.workspaceDataSourceService.getSchemaName(workspaceId); From bdcea2cba5ad884163a89262a1ed282da4c649d1 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 15:35:47 +0200 Subject: [PATCH 21/47] Remove USER_CREATION --- ...sePageChangeEffectNavigateLocation.test.ts | 46 ++++++++++--------- .../usePageChangeEffectNavigateLocation.ts | 5 +- .../__test__/useOnboardingStatus.test.ts | 32 ++++++------- .../modules/auth/hooks/useOnboardingStatus.ts | 2 +- .../hooks/__tests__/useShowAuthModal.test.tsx | 46 ++++++++++--------- .../ui/layout/hooks/useShowAuthModal.ts | 2 +- .../enums/onboarding-status.enum.ts | 1 - .../onboarding/onboarding.service.ts | 6 +-- 8 files changed, 66 insertions(+), 74 deletions(-) diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 4666a97a725..59b0a097c0e 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -6,7 +6,9 @@ import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation'; jest.mock('@/auth/hooks/useOnboardingStatus'); -const setupMockOnboardingStatus = (onboardingStatus: OnboardingStatus) => { +const setupMockOnboardingStatus = ( + onboardingStatus: OnboardingStatus | undefined, +) => { jest.mocked(useOnboardingStatus).mockReturnValueOnce(onboardingStatus); }; @@ -32,7 +34,7 @@ const testCases = [ { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.Verify, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.Verify, status: undefined, res: undefined }, { loc: AppPath.Verify, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.Verify, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.Verify, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -44,7 +46,7 @@ const testCases = [ { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.SignInUp, status: undefined, res: undefined }, { loc: AppPath.SignInUp, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.SignInUp, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.SignInUp, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -56,7 +58,7 @@ const testCases = [ { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.Invite, status: undefined, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.WorkspaceActivation, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.ProfileCreation, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.SyncEmail, res: undefined }, @@ -68,7 +70,7 @@ const testCases = [ { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.UserCreation, res: undefined }, + { loc: AppPath.ResetPassword, status: undefined, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.WorkspaceActivation, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.ProfileCreation, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SyncEmail, res: undefined }, @@ -80,7 +82,7 @@ const testCases = [ { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.CreateWorkspace, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.WorkspaceActivation, res: undefined }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -92,7 +94,7 @@ const testCases = [ { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.CreateProfile, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.CreateProfile, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.CreateProfile, status: OnboardingStatus.ProfileCreation, res: undefined }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -104,7 +106,7 @@ const testCases = [ { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.SyncEmails, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.SyncEmails, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.SyncEmails, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SyncEmail, res: undefined }, @@ -116,7 +118,7 @@ const testCases = [ { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.InviteTeam, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.InviteTeam, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.InviteTeam, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -128,7 +130,7 @@ const testCases = [ { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequired, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.PlanRequired, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.PlanRequired, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -140,7 +142,7 @@ const testCases = [ { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequiredSuccess, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.WorkspaceActivation, res: undefined }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -152,7 +154,7 @@ const testCases = [ { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Index, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.Index, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.Index, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.Index, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -164,7 +166,7 @@ const testCases = [ { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.TasksPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.TasksPage, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.TasksPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.TasksPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.TasksPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -176,7 +178,7 @@ const testCases = [ { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.OpportunitiesPage, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -188,7 +190,7 @@ const testCases = [ { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.RecordIndexPage, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -200,7 +202,7 @@ const testCases = [ { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.RecordShowPage, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -212,7 +214,7 @@ const testCases = [ { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.SettingsCatchAll, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -224,7 +226,7 @@ const testCases = [ { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.DevelopersCatchAll, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -236,7 +238,7 @@ const testCases = [ { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Impersonate, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.Impersonate, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.Impersonate, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.Impersonate, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -248,7 +250,7 @@ const testCases = [ { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.Authorize, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.Authorize, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.Authorize, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.Authorize, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -260,7 +262,7 @@ const testCases = [ { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.NotFoundWildcard, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, @@ -272,7 +274,7 @@ const testCases = [ { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStatus.UserCreation, res: AppPath.SignInUp }, + { loc: AppPath.NotFound, status: undefined, res: AppPath.SignInUp }, { loc: AppPath.NotFound, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, { loc: AppPath.NotFound, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, { loc: AppPath.NotFound, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index 5f1a2f6b7f4..9a7e371b835 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -32,10 +32,7 @@ export const usePageChangeEffectNavigateLocation = () => { return; } - if ( - onboardingStatus === OnboardingStatus.UserCreation && - !isMatchingOngoingUserCreationRoute - ) { + if (!onboardingStatus && !isMatchingOngoingUserCreationRoute) { return AppPath.SignInUp; } diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index a617a92fb2d..b0bf5b4d3a1 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -37,29 +37,25 @@ const renderHooks = () => { }; describe('useOnboardingStatus', () => { - it(`should return "${OnboardingStatus.UserCreation}" when user is not logged in`, async () => { + it(`should return "undefined" when user is not logged in`, async () => { const { result } = renderHooks(); - expect(result.current.onboardingStatus).toBe(OnboardingStatus.UserCreation); + expect(result.current.onboardingStatus).toBe(undefined); }); - Object.values(OnboardingStatus) - .filter( - (onboardingStatus) => onboardingStatus !== OnboardingStatus.UserCreation, - ) - .forEach((onboardingStatus) => { - it(`should return "${onboardingStatus}"`, async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentUser } = result.current; + Object.values(OnboardingStatus).forEach((onboardingStatus) => { + it(`should return "${onboardingStatus}"`, async () => { + const { result } = renderHooks(); + const { setTokenPair, setCurrentUser } = result.current; - act(() => { - setTokenPair(tokenPair); - setCurrentUser({ - ...currentUser, - onboardingStatus, - }); + act(() => { + setTokenPair(tokenPair); + setCurrentUser({ + ...currentUser, + onboardingStatus, }); - - expect(result.current.onboardingStatus).toBe(onboardingStatus); }); + + expect(result.current.onboardingStatus).toBe(onboardingStatus); }); + }); }); diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts index 8c7edea3a16..3e4828fc93b 100644 --- a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts @@ -8,7 +8,7 @@ export const useOnboardingStatus = (): OnboardingStatus | null | undefined => { const currentUser = useRecoilValue(currentUserState); const isLoggedIn = useIsLogged(); if (!isLoggedIn) { - return OnboardingStatus.UserCreation; + return undefined; } return currentUser?.onboardingStatus; }; diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index 87f81bd88c1..747b36fd437 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -9,7 +9,9 @@ import { OnboardingStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; jest.mock('@/auth/hooks/useOnboardingStatus'); -const setupMockOnboardingStatus = (onboardingStatus: OnboardingStatus) => { +const setupMockOnboardingStatus = ( + onboardingStatus: OnboardingStatus | undefined, +) => { jest.mocked(useOnboardingStatus).mockReturnValueOnce(onboardingStatus); }; @@ -43,7 +45,7 @@ const testCases = [ { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.UserCreation, res: false }, + { loc: AppPath.Verify, status: undefined, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.WorkspaceActivation, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.ProfileCreation, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.SyncEmail, res: false }, @@ -55,7 +57,7 @@ const testCases = [ { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.SignInUp, status: undefined, res: true }, { loc: AppPath.SignInUp, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.SignInUp, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.SignInUp, status: OnboardingStatus.SyncEmail, res: true }, @@ -67,7 +69,7 @@ const testCases = [ { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Invite, status: undefined, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.SyncEmail, res: true }, @@ -79,7 +81,7 @@ const testCases = [ { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.ResetPassword, status: undefined, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SyncEmail, res: true }, @@ -91,7 +93,7 @@ const testCases = [ { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.CreateWorkspace, status: undefined, res: true }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SyncEmail, res: true }, @@ -103,7 +105,7 @@ const testCases = [ { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.CreateProfile, status: undefined, res: true }, { loc: AppPath.CreateProfile, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.CreateProfile, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SyncEmail, res: true }, @@ -115,7 +117,7 @@ const testCases = [ { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.SyncEmails, status: undefined, res: true }, { loc: AppPath.SyncEmails, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.SyncEmails, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SyncEmail, res: true }, @@ -127,7 +129,7 @@ const testCases = [ { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.InviteTeam, status: undefined, res: true }, { loc: AppPath.InviteTeam, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.InviteTeam, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SyncEmail, res: true }, @@ -139,7 +141,7 @@ const testCases = [ { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: true }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.PlanRequired, status: undefined, res: true }, { loc: AppPath.PlanRequired, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.PlanRequired, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SyncEmail, res: true }, @@ -151,7 +153,7 @@ const testCases = [ { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: undefined, res: true }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SyncEmail, res: true }, @@ -163,7 +165,7 @@ const testCases = [ { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Index, status: undefined, res: true }, { loc: AppPath.Index, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.Index, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.Index, status: OnboardingStatus.SyncEmail, res: true }, @@ -175,7 +177,7 @@ const testCases = [ { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.TasksPage, status: undefined, res: true }, { loc: AppPath.TasksPage, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.TasksPage, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.TasksPage, status: OnboardingStatus.SyncEmail, res: true }, @@ -187,7 +189,7 @@ const testCases = [ { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.OpportunitiesPage, status: undefined, res: true }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SyncEmail, res: true }, @@ -199,7 +201,7 @@ const testCases = [ { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.RecordIndexPage, status: undefined, res: true }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SyncEmail, res: true }, @@ -211,7 +213,7 @@ const testCases = [ { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.RecordShowPage, status: undefined, res: true }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SyncEmail, res: true }, @@ -223,7 +225,7 @@ const testCases = [ { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.SettingsCatchAll, status: undefined, res: true }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SyncEmail, res: true }, @@ -235,7 +237,7 @@ const testCases = [ { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.DevelopersCatchAll, status: undefined, res: true }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SyncEmail, res: true }, @@ -247,7 +249,7 @@ const testCases = [ { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Impersonate, status: undefined, res: true }, { loc: AppPath.Impersonate, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.Impersonate, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.Impersonate, status: OnboardingStatus.SyncEmail, res: true }, @@ -259,7 +261,7 @@ const testCases = [ { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.Authorize, status: undefined, res: true }, { loc: AppPath.Authorize, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.Authorize, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.Authorize, status: OnboardingStatus.SyncEmail, res: true }, @@ -271,7 +273,7 @@ const testCases = [ { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.NotFoundWildcard, status: undefined, res: true }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SyncEmail, res: true }, @@ -283,7 +285,7 @@ const testCases = [ { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.UserCreation, res: true }, + { loc: AppPath.NotFound, status: undefined, res: true }, { loc: AppPath.NotFound, status: OnboardingStatus.WorkspaceActivation, res: true }, { loc: AppPath.NotFound, status: OnboardingStatus.ProfileCreation, res: true }, { loc: AppPath.NotFound, status: OnboardingStatus.SyncEmail, res: true }, diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index f7f32a1c3a3..2c72fd59d0c 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -24,8 +24,8 @@ export const useShowAuthModal = () => { return isDefaultLayoutAuthModalVisible; } if ( + !onboardingStatus || onboardingStatus === OnboardingStatus.SubscriptionIncomplete || - onboardingStatus === OnboardingStatus.UserCreation || onboardingStatus === OnboardingStatus.ProfileCreation || onboardingStatus === OnboardingStatus.WorkspaceActivation || onboardingStatus === OnboardingStatus.SyncEmail || diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts index 6e7311c4a47..1086f9a31df 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts @@ -3,7 +3,6 @@ export enum OnboardingStatus { SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED', SUBSCRIPTION_PAST_DUE = 'SUBSCRIPTION_PAST_DUE', SUBSCRIPTION_UNPAID = 'SUBSCRIPTION_UNPAID', - USER_CREATION = 'USER_CREATION', WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 76adf506bf8..6c65f2def2f 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -132,11 +132,7 @@ export class OnboardingService { ); } - async getOnboardingStatus(user?: User) { - if (!user) { - return OnboardingStatus.USER_CREATION; - } - + async getOnboardingStatus(user: User) { if (await this.isSubscriptionIncompleteOnboardingStatus(user)) { return OnboardingStatus.SUBSCRIPTION_INCOMPLETE; } From 2996d7b8e082c71e832366cf960d2514966e93a9 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 15:54:29 +0200 Subject: [PATCH 22/47] Remove as any --- .../hooks/useSetNextOnboardingStatus.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index 36fda57c2c3..be7b6709830 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -9,6 +9,7 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSi import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; import { OnboardingStatus } from '~/generated/graphql'; +import { isDefined } from '~/utils/isDefined'; const getNextOnboardingStatus = ( currentUser: CurrentUser | null, @@ -44,14 +45,15 @@ export const useSetNextOnboardingStatus = () => { workspaceMembers, currentWorkspace, ); - set( - currentUserState, - (current) => - ({ + set(currentUserState, (current) => { + if (isDefined(current)) { + return { ...current, - onboardingStatus: nextOnboardingStatus, - }) as any, - ); + onboardingStatus: nextOnboardingStatus as OnboardingStatus, + }; + } + return current; + }); }, [workspaceMembers, currentWorkspace], ); From 5280d7823a270d5ad264aaba123ac0cb72f9c88b Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 15:58:08 +0200 Subject: [PATCH 23/47] Remove as any 2 --- .../src/pages/onboarding/CreateProfile.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx index 7cfb5e0ded0..49c7286be5b 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx @@ -23,6 +23,7 @@ import { TextInputV2 } from '@/ui/input/components/TextInputV2'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; import { OnboardingStatus } from '~/generated/graphql'; +import { isDefined } from '~/utils/isDefined'; const StyledContentContainer = styled.div` width: 100%; @@ -101,17 +102,19 @@ export const CreateProfile = () => { }, }); - setCurrentWorkspaceMember( - (current) => - ({ + setCurrentWorkspaceMember((current) => { + if (isDefined(current)) { + return { ...current, name: { firstName: data.firstName, lastName: data.lastName, }, colorScheme: 'System', - }) as any, - ); + }; + } + return current; + }); setNextOnboardingStatus(); } catch (error: any) { enqueueSnackBar(error?.message, { From b737100ee78b25b0beb0fa9af5e61f3a24f0871a Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 16:11:54 +0200 Subject: [PATCH 24/47] Update generated types --- .../src/generated/graphql.tsx | 98 ++++++++++++++----- .../src/generated-metadata/graphql.ts | 1 - .../twenty-front/src/generated/graphql.tsx | 1 - 3 files changed, 74 insertions(+), 26 deletions(-) diff --git a/packages/twenty-chrome-extension/src/generated/graphql.tsx b/packages/twenty-chrome-extension/src/generated/graphql.tsx index e608e55928a..a1a13672ff4 100644 --- a/packages/twenty-chrome-extension/src/generated/graphql.tsx +++ b/packages/twenty-chrome-extension/src/generated/graphql.tsx @@ -992,6 +992,12 @@ export type CalendarChannel = { isSyncEnabled?: Maybe; /** Sync Cursor. Used for syncing events from the calendar provider */ syncCursor?: Maybe; + /** Sync stage */ + syncStage?: Maybe; + /** Sync stage started at */ + syncStageStartedAt?: Maybe; + /** Sync status */ + syncStatus?: Maybe; /** Throttle Failure Count */ throttleFailureCount?: Maybe; /** Update date */ @@ -1036,6 +1042,12 @@ export type CalendarChannelCreateInput = { isSyncEnabled?: InputMaybe; /** Sync Cursor. Used for syncing events from the calendar provider */ syncCursor?: InputMaybe; + /** Sync stage */ + syncStage?: InputMaybe; + /** Sync stage started at */ + syncStageStartedAt?: InputMaybe; + /** Sync status */ + syncStatus?: InputMaybe; /** Throttle Failure Count */ throttleFailureCount?: InputMaybe; /** Update date */ @@ -1170,6 +1182,12 @@ export type CalendarChannelFilterInput = { or?: InputMaybe>>; /** Sync Cursor. Used for syncing events from the calendar provider */ syncCursor?: InputMaybe; + /** Sync stage */ + syncStage?: InputMaybe; + /** Sync stage started at */ + syncStageStartedAt?: InputMaybe; + /** Sync status */ + syncStatus?: InputMaybe; /** Throttle Failure Count */ throttleFailureCount?: InputMaybe; /** Update date */ @@ -1194,6 +1212,12 @@ export type CalendarChannelOrderByInput = { isSyncEnabled?: InputMaybe; /** Sync Cursor. Used for syncing events from the calendar provider */ syncCursor?: InputMaybe; + /** Sync stage */ + syncStage?: InputMaybe; + /** Sync stage started at */ + syncStageStartedAt?: InputMaybe; + /** Sync status */ + syncStatus?: InputMaybe; /** Throttle Failure Count */ throttleFailureCount?: InputMaybe; /** Update date */ @@ -1202,6 +1226,50 @@ export type CalendarChannelOrderByInput = { visibility?: InputMaybe; }; +/** Sync stage */ +export enum CalendarChannelSyncStageEnum { + /** Calendar events import ongoing */ + CalendarEventsImportOngoing = 'CALENDAR_EVENTS_IMPORT_ONGOING', + /** Calendar events import pending */ + CalendarEventsImportPending = 'CALENDAR_EVENTS_IMPORT_PENDING', + /** Calendar event list fetch ongoing */ + CalendarEventListFetchOngoing = 'CALENDAR_EVENT_LIST_FETCH_ONGOING', + /** Failed */ + Failed = 'FAILED', + /** Full calendar event list fetch pending */ + FullCalendarEventListFetchPending = 'FULL_CALENDAR_EVENT_LIST_FETCH_PENDING', + /** Partial calendar event list fetch pending */ + PartialCalendarEventListFetchPending = 'PARTIAL_CALENDAR_EVENT_LIST_FETCH_PENDING' +} + +export type CalendarChannelSyncStageEnumFilter = { + eq?: InputMaybe; + in?: InputMaybe>>; + is?: InputMaybe; + neq?: InputMaybe; +}; + +/** Sync status */ +export enum CalendarChannelSyncStatusEnum { + /** Active */ + Active = 'ACTIVE', + /** Failed Insufficient Permissions */ + FailedInsufficientPermissions = 'FAILED_INSUFFICIENT_PERMISSIONS', + /** Failed Unknown */ + FailedUnknown = 'FAILED_UNKNOWN', + /** Not Synced */ + NotSynced = 'NOT_SYNCED', + /** Ongoing */ + Ongoing = 'ONGOING' +} + +export type CalendarChannelSyncStatusEnumFilter = { + eq?: InputMaybe; + in?: InputMaybe>>; + is?: InputMaybe; + neq?: InputMaybe; +}; + /** Calendar Channels */ export type CalendarChannelUpdateInput = { /** Connected Account id foreign key */ @@ -1218,6 +1286,12 @@ export type CalendarChannelUpdateInput = { isSyncEnabled?: InputMaybe; /** Sync Cursor. Used for syncing events from the calendar provider */ syncCursor?: InputMaybe; + /** Sync stage */ + syncStage?: InputMaybe; + /** Sync stage started at */ + syncStageStartedAt?: InputMaybe; + /** Sync status */ + syncStatus?: InputMaybe; /** Throttle Failure Count */ throttleFailureCount?: InputMaybe; /** Update date */ @@ -1786,7 +1860,6 @@ export type Company = { idealCustomerProfile?: Maybe; /** The company Linkedin account */ linkedinLink?: Maybe; - multiSelect?: Maybe>>; /** The company name */ name?: Maybe; /** Opportunities linked to the company. */ @@ -1903,7 +1976,6 @@ export type CompanyCreateInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; - multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; /** Company record position */ @@ -1941,7 +2013,6 @@ export type CompanyFilterInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; - multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; not?: InputMaybe; @@ -1954,24 +2025,6 @@ export type CompanyFilterInput = { xLink?: InputMaybe; }; -export enum CompanyMultiSelectEnum { - /** Option 1 */ - Option_1 = 'OPTION_1', - /** Option 2 */ - Option_2 = 'OPTION_2', - /** Option 3 */ - Option_3 = 'OPTION_3', - /** Option 4 */ - Option_4 = 'OPTION_4' -} - -export type CompanyMultiSelectEnumFilter = { - eq?: InputMaybe; - in?: InputMaybe>>; - is?: InputMaybe; - neq?: InputMaybe; -}; - /** A company */ export type CompanyOrderByInput = { /** Your team member responsible for managing the company account id foreign key */ @@ -1992,7 +2045,6 @@ export type CompanyOrderByInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; - multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; /** Company record position */ @@ -2023,7 +2075,6 @@ export type CompanyUpdateInput = { idealCustomerProfile?: InputMaybe; /** The company Linkedin account */ linkedinLink?: InputMaybe; - multiSelect?: InputMaybe>>; /** The company name */ name?: InputMaybe; /** Company record position */ @@ -4839,7 +4890,6 @@ export enum OnboardingStatus { SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', - UserCreation = 'USER_CREATION', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index e08f6c3ea0b..2a9c888689c 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -649,7 +649,6 @@ export enum OnboardingStatus { SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', - UserCreation = 'USER_CREATION', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 8021ef84f89..bb267ec9139 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -472,7 +472,6 @@ export enum OnboardingStatus { SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', - UserCreation = 'USER_CREATION', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } From 19a5350bfc6ad5379b7db56281698a15a0db07cd Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 16:56:33 +0200 Subject: [PATCH 25/47] Use enum for subscription status and interval --- ...23-useEnumForSubscriptionStatusInterval.ts | 61 +++++++++++++++++++ .../auth/services/sign-in-up.service.ts | 5 +- .../core-modules/billing/billing.service.ts | 9 ++- .../entities/billing-subscription.entity.ts | 39 ++++++++++-- .../onboarding/onboarding.service.ts | 9 +-- .../workspace/workspace.entity.ts | 14 ++++- .../delete-incomplete-workspaces.command.ts | 3 +- .../jobs/google-calendar-sync.cron.job.ts | 7 ++- .../messaging-message-list-fetch.cron.job.ts | 7 ++- .../messaging-messages-import.cron.job.ts | 7 ++- .../jobs/messaging-ongoing-stale.cron.job.ts | 7 ++- ...age-channel-sync-status-monitoring.cron.ts | 7 ++- 12 files changed, 153 insertions(+), 22 deletions(-) create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/1719327438923-useEnumForSubscriptionStatusInterval.ts diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1719327438923-useEnumForSubscriptionStatusInterval.ts b/packages/twenty-server/src/database/typeorm/core/migrations/1719327438923-useEnumForSubscriptionStatusInterval.ts new file mode 100644 index 00000000000..c2894f0fd48 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/1719327438923-useEnumForSubscriptionStatusInterval.ts @@ -0,0 +1,61 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UseEnumForSubscriptionStatusInterval1719327438923 + implements MigrationInterface +{ + name = 'UseEnumForSubscriptionStatusInterval1719327438923'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "core"."billingSubscription_status_enum" AS ENUM('active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'paused', 'trialing', 'unpaid')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE "core"."billingSubscription_status_enum" USING "status"::"core"."billingSubscription_status_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" SET NOT NULL`, + ); + await queryRunner.query( + `CREATE TYPE "core"."billingSubscription_interval_enum" AS ENUM('day', 'month', 'week', 'year')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE "core"."billingSubscription_interval_enum" USING "interval"::"core"."billingSubscription_interval_enum"`, + ); + await queryRunner.query( + `CREATE TYPE "core"."workspace_subscriptionstatus_enum" AS ENUM('active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'paused', 'trialing', 'unpaid')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."workspace" ALTER COLUMN "subscriptionStatus" DROP DEFAULT`, + ); + await queryRunner.query( + `ALTER TABLE "core"."workspace" ALTER COLUMN "subscriptionStatus" TYPE "core"."workspace_subscriptionstatus_enum" USING "subscriptionStatus"::"core"."workspace_subscriptionstatus_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."workspace" ALTER COLUMN "subscriptionStatus" SET NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "core"."workspace" ALTER COLUMN "subscriptionStatus" SET DEFAULT 'incomplete'::"core"."workspace_subscriptionstatus_enum"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."workspace" ALTER COLUMN "subscriptionStatus" TYPE text`, + ); + await queryRunner.query( + `DROP TYPE "core"."workspace_subscriptionstatus_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "interval" TYPE text`, + ); + await queryRunner.query( + `DROP TYPE "core"."billingSubscription_interval_enum"`, + ); + await queryRunner.query( + `ALTER TABLE "core"."billingSubscription" ALTER COLUMN "status" TYPE text`, + ); + await queryRunner.query( + `DROP TYPE "core"."billingSubscription_status_enum"`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts index 3397f260336..68aae31ac7f 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts @@ -12,6 +12,7 @@ import FileType from 'file-type'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { assert } from 'src/utils/assert'; import { PASSWORD_REGEX, @@ -144,7 +145,7 @@ export class SignInUpService { assert( !this.environmentService.get('IS_BILLING_ENABLED') || - workspace.subscriptionStatus !== 'incomplete', + workspace.subscriptionStatus !== SubscriptionStatus.Incomplete, 'Workspace subscription status is incomplete', ForbiddenException, ); @@ -199,7 +200,7 @@ export class SignInUpService { displayName: '', domainName: '', inviteHash: v4(), - subscriptionStatus: 'incomplete', + subscriptionStatus: SubscriptionStatus.Incomplete, }); const workspace = await this.workspaceRepository.save(workspaceToCreate); diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index f4494c0eaa4..4a2c296e98d 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -6,7 +6,10 @@ import { Not, Repository } from 'typeorm'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.service'; -import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { + BillingSubscription, + SubscriptionInterval, +} from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { ProductPriceEntity } from 'src/engine/core-modules/billing/dto/product-price.entity'; @@ -171,7 +174,9 @@ export class BillingService { workspaceId: user.defaultWorkspaceId, }); const newInterval = - billingSubscription?.interval === 'year' ? 'month' : 'year'; + billingSubscription?.interval === SubscriptionInterval.Year + ? SubscriptionInterval.Month + : SubscriptionInterval.Year; const billingSubscriptionItem = await this.getBillingSubscriptionItem( user.defaultWorkspaceId, ); diff --git a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts index 4da2b3ef568..509d5250b26 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/entities/billing-subscription.entity.ts @@ -1,4 +1,4 @@ -import { Field, ObjectType } from '@nestjs/graphql'; +import { Field, ObjectType, registerEnumType } from '@nestjs/graphql'; import { Column, @@ -18,6 +18,27 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; +export enum SubscriptionStatus { + Active = 'active', + Canceled = 'canceled', + Incomplete = 'incomplete', + IncompleteExpired = 'incomplete_expired', + PastDue = 'past_due', + Paused = 'paused', + Trialing = 'trialing', + Unpaid = 'unpaid', +} + +export enum SubscriptionInterval { + Day = 'day', + Month = 'month', + Week = 'week', + Year = 'year', +} + +registerEnumType(SubscriptionStatus, { name: 'SubscriptionStatus' }); +registerEnumType(SubscriptionInterval, { name: 'SubscriptionInterval' }); + @Entity({ name: 'billingSubscription', schema: 'core' }) @ObjectType('BillingSubscription') export class BillingSubscription { @@ -49,12 +70,20 @@ export class BillingSubscription { @Column({ unique: true, nullable: false }) stripeSubscriptionId: string; - @Field(() => String) - @Column({ type: 'text', nullable: false }) + @Field(() => SubscriptionStatus) + @Column({ + type: 'enum', + enum: Object.values(SubscriptionStatus), + nullable: false, + }) status: Stripe.Subscription.Status; - @Field(() => String, { nullable: true }) - @Column({ type: 'text', nullable: true }) + @Field(() => SubscriptionInterval, { nullable: true }) + @Column({ + type: 'enum', + enum: Object.values(SubscriptionInterval), + nullable: true, + }) interval: Stripe.Price.Recurring.Interval; @OneToMany( diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 6c65f2def2f..d5eb9ebbcf8 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -14,6 +14,7 @@ import { EnvironmentService } from 'src/engine/integrations/environment/environm import { BillingService } from 'src/engine/core-modules/billing/billing.service'; import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; enum OnboardingStepValues { SKIPPED = 'SKIPPED', @@ -46,7 +47,7 @@ export class OnboardingService { private async isSubscriptionIncompleteOnboardingStatus(user: User) { return ( this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === 'incomplete' + user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.Incomplete ); } @@ -102,21 +103,21 @@ export class OnboardingService { private async isSubscriptionCanceledOnboardingStatus(user: User) { return ( this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === 'canceled' + user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.Canceled ); } private async isSubscriptionPastDueOnboardingStatus(user: User) { return ( this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === 'past_due' + user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.PastDue ); } private async isSubscriptionUnpaidOnboardingStatus(user: User) { return ( this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === 'unpaid' + user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.Unpaid ); } diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts index edc141b5580..42e6a0d4fa6 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts @@ -14,7 +14,10 @@ import Stripe from 'stripe'; import { User } from 'src/engine/core-modules/user/user.entity'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { + BillingSubscription, + SubscriptionStatus, +} from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; @@ -85,8 +88,13 @@ export class Workspace { @OneToMany(() => FeatureFlagEntity, (featureFlag) => featureFlag.workspace) featureFlags: Relation; - @Field(() => String) - @Column({ type: 'text', default: 'incomplete' }) + @Field(() => SubscriptionStatus) + @Column({ + type: 'enum', + enum: Object.values(SubscriptionStatus), + default: SubscriptionStatus.Incomplete, + nullable: false, + }) subscriptionStatus: Stripe.Subscription.Status; @Field({ nullable: true }) diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts index 30c980c4e0c..4b2252a2b3c 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts @@ -8,6 +8,7 @@ import { WorkspaceService } from 'src/engine/core-modules/workspace/services/wor import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { getDryRunLogHeader } from 'src/utils/get-dry-run-log-header'; import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; type DeleteIncompleteWorkspacesCommandOptions = { dryRun?: boolean; @@ -52,7 +53,7 @@ export class DeleteIncompleteWorkspacesCommand extends CommandRunner { options: DeleteIncompleteWorkspacesCommandOptions, ): Promise { const where: FindOptionsWhere = { - subscriptionStatus: 'incomplete', + subscriptionStatus: SubscriptionStatus.Incomplete, }; if (options.workspaceIds) { diff --git a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts index b2c6e43da7f..b86f1453f83 100644 --- a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts +++ b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts @@ -10,6 +10,7 @@ import { EnvironmentService } from 'src/engine/integrations/environment/environm import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants'; import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator'; import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; @Processor({ queueName: MessageQueue.cronQueue, @@ -31,7 +32,11 @@ export class GoogleCalendarSyncCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In(['active', 'trialing', 'past_due']), + subscriptionStatus: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts index 9a3adfa50bf..bd7c99b1449 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts @@ -21,6 +21,7 @@ import { import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator'; import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator'; import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; @Processor(MessageQueue.cronQueue) export class MessagingMessageListFetchCronJob { @@ -44,7 +45,11 @@ export class MessagingMessageListFetchCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In(['active', 'trialing', 'past_due']), + subscriptionStatus: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts index 53c9d86d90b..f578227b58d 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts @@ -21,6 +21,7 @@ import { MessageChannelSyncStage, MessageChannelWorkspaceEntity, } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; @Processor(MessageQueue.cronQueue) export class MessagingMessagesImportCronJob { @@ -44,7 +45,11 @@ export class MessagingMessagesImportCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In(['active', 'trialing', 'past_due']), + subscriptionStatus: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts index b9666c5ec56..4e082ff1c67 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts @@ -14,6 +14,7 @@ import { MessagingOngoingStaleJobData, MessagingOngoingStaleJob, } from 'src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; @Processor(MessageQueue.cronQueue) export class MessagingOngoingStaleCronJob { @@ -33,7 +34,11 @@ export class MessagingOngoingStaleCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In(['active', 'trialing', 'past_due']), + subscriptionStatus: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts index 584792ac4f2..63cacdec7a4 100644 --- a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts +++ b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts @@ -14,6 +14,7 @@ import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/stan import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator'; import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; import { MessagingTelemetryService } from 'src/modules/messaging/common/services/messaging-telemetry.service'; +import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; @Processor(MessageQueue.cronQueue) export class MessagingMessageChannelSyncStatusMonitoringCronJob { @@ -45,7 +46,11 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In(['active', 'trialing', 'past_due']), + subscriptionStatus: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), } : {}, select: ['id'], From 7343466c375eca5227f4123f7725b904ec916455 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 18:03:09 +0200 Subject: [PATCH 26/47] Fix orm error --- .../core-modules/user-workspace/user-workspace.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts index 1ba4e66269d..cc48e5eae5f 100644 --- a/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts +++ b/packages/twenty-server/src/engine/core-modules/user-workspace/user-workspace.service.ts @@ -102,9 +102,11 @@ export class UserWorkspaceService extends TypeOrmQueryService { } public async getWorkspaceMemberCount(): Promise { - const workspaceMemberCount = await this.workspaceMemberRepository.count(); + if (!this.workspaceMemberRepository) { + return undefined; + } - return workspaceMemberCount; + return await this.workspaceMemberRepository.count(); } async checkUserWorkspaceExists( From dc48ec0948d14615a2ef8484ed91052972e689cf Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 25 Jun 2024 18:19:09 +0200 Subject: [PATCH 27/47] Use enum for subscription status and interval 2 --- .../twenty-front/src/generated/graphql.tsx | 44 +++++++++++++------ .../billing/graphql/checkoutSession.ts | 2 +- .../src/pages/onboarding/ChooseYourPlan.tsx | 9 ++-- .../src/pages/settings/SettingsBilling.tsx | 10 +++-- .../core-modules/billing/billing.service.ts | 3 +- .../billing/dto/checkout-session.input.ts | 4 +- .../billing/dto/product-price.entity.ts | 4 +- 7 files changed, 51 insertions(+), 25 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index bb267ec9139..c4c0ad5629c 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -92,8 +92,8 @@ export type Billing = { export type BillingSubscription = { __typename?: 'BillingSubscription'; id: Scalars['UUID']; - interval?: Maybe; - status: Scalars['String']; + interval?: Maybe; + status: SubscriptionStatus; }; export type BillingSubscriptionFilter = { @@ -341,7 +341,7 @@ export type MutationChallengeArgs = { export type MutationCheckoutSessionArgs = { - recurringInterval: Scalars['String']; + recurringInterval: SubscriptionInterval; successUrlPath?: InputMaybe; }; @@ -504,7 +504,7 @@ export type PostgresCredentials = { export type ProductPriceEntity = { __typename?: 'ProductPriceEntity'; created: Scalars['Float']; - recurringInterval: Scalars['String']; + recurringInterval: SubscriptionInterval; stripePriceId: Scalars['String']; unitAmount: Scalars['Float']; }; @@ -686,6 +686,24 @@ export enum SortNulls { NullsLast = 'NULLS_LAST' } +export enum SubscriptionInterval { + Day = 'Day', + Month = 'Month', + Week = 'Week', + Year = 'Year' +} + +export enum SubscriptionStatus { + Active = 'Active', + Canceled = 'Canceled', + Incomplete = 'Incomplete', + IncompleteExpired = 'IncompleteExpired', + PastDue = 'PastDue', + Paused = 'Paused', + Trialing = 'Trialing', + Unpaid = 'Unpaid' +} + export type Support = { __typename?: 'Support'; supportDriver: Scalars['String']; @@ -898,7 +916,7 @@ export type Workspace = { id: Scalars['UUID']; inviteHash?: Maybe; logo?: Maybe; - subscriptionStatus: Scalars['String']; + subscriptionStatus: SubscriptionStatus; updatedAt: Scalars['DateTime']; }; @@ -1158,7 +1176,7 @@ export type ImpersonateMutationVariables = Exact<{ }>; -export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type RenewTokenMutationVariables = Exact<{ appToken: Scalars['String']; @@ -1190,7 +1208,7 @@ export type VerifyMutationVariables = Exact<{ }>; -export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type CheckUserExistsQueryVariables = Exact<{ email: Scalars['String']; @@ -1215,7 +1233,7 @@ export type BillingPortalSessionQueryVariables = Exact<{ export type BillingPortalSessionQuery = { __typename?: 'Query', billingPortalSession: { __typename?: 'SessionEntity', url?: string | null } }; export type CheckoutSessionMutationVariables = Exact<{ - recurringInterval: Scalars['String']; + recurringInterval: SubscriptionInterval; successUrlPath?: InputMaybe; }>; @@ -1227,7 +1245,7 @@ export type GetProductPricesQueryVariables = Exact<{ }>; -export type GetProductPricesQuery = { __typename?: 'Query', getProductPrices: { __typename?: 'ProductPricesEntity', productPrices: Array<{ __typename?: 'ProductPriceEntity', created: number, recurringInterval: string, stripePriceId: string, unitAmount: number }> } }; +export type GetProductPricesQuery = { __typename?: 'Query', getProductPrices: { __typename?: 'ProductPricesEntity', productPrices: Array<{ __typename?: 'ProductPriceEntity', created: number, recurringInterval: SubscriptionInterval, stripePriceId: string, unitAmount: number }> } }; export type UpdateBillingSubscriptionMutationVariables = Exact<{ [key: string]: never; }>; @@ -1244,7 +1262,7 @@ export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string] export type SkipSyncEmailOnboardingStepMutation = { __typename?: 'Mutation', skipSyncEmailOnboardingStep: { __typename?: 'OnboardingStepSuccess', success: boolean } }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }; export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>; @@ -1261,7 +1279,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, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: string, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: string, interval?: string | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } }; export type AddUserToWorkspaceMutationVariables = Exact<{ inviteHash: Scalars['String']; @@ -1294,7 +1312,7 @@ export type UpdateWorkspaceMutationVariables = Exact<{ }>; -export type UpdateWorkspaceMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: any, domainName?: string | null, displayName?: string | null, logo?: string | null, allowImpersonation: boolean, subscriptionStatus: string } }; +export type UpdateWorkspaceMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: any, domainName?: string | null, displayName?: string | null, logo?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus } }; export type UploadWorkspaceLogoMutationVariables = Exact<{ file: Scalars['Upload']; @@ -2223,7 +2241,7 @@ export type BillingPortalSessionQueryHookResult = ReturnType; export type BillingPortalSessionQueryResult = Apollo.QueryResult; export const CheckoutSessionDocument = gql` - mutation CheckoutSession($recurringInterval: String!, $successUrlPath: String) { + mutation CheckoutSession($recurringInterval: SubscriptionInterval!, $successUrlPath: String) { checkoutSession( recurringInterval: $recurringInterval successUrlPath: $successUrlPath diff --git a/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts b/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts index bff619a82f8..53821543cef 100644 --- a/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts +++ b/packages/twenty-front/src/modules/billing/graphql/checkoutSession.ts @@ -2,7 +2,7 @@ import { gql } from '@apollo/client'; export const CHECKOUT_SESSION = gql` mutation CheckoutSession( - $recurringInterval: String! + $recurringInterval: SubscriptionInterval! $successUrlPath: String ) { checkoutSession( diff --git a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx index cd65b729f9d..02fc25fbb99 100644 --- a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx +++ b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx @@ -19,6 +19,7 @@ import { ActionLink } from '@/ui/navigation/link/components/ActionLink'; import { CAL_LINK } from '@/ui/navigation/link/constants/Cal'; import { ProductPriceEntity, + SubscriptionInterval, useCheckoutSessionMutation, useGetProductPricesQuery, } from '~/generated/graphql'; @@ -75,7 +76,7 @@ const benefits = [ export const ChooseYourPlan = () => { const billing = useRecoilValue(billingState); - const [planSelected, setPlanSelected] = useState('month'); + const [planSelected, setPlanSelected] = useState(SubscriptionInterval.Month); const [isSubmitting, setIsSubmitting] = useState(false); @@ -87,7 +88,7 @@ export const ChooseYourPlan = () => { const [checkoutSession] = useCheckoutSessionMutation(); - const handlePlanChange = (type?: string) => { + const handlePlanChange = (type?: SubscriptionInterval) => { return () => { if (isNonEmptyString(type) && planSelected !== type) { setPlanSelected(type); @@ -101,11 +102,11 @@ export const ChooseYourPlan = () => { price: ProductPriceEntity, prices: ProductPriceEntity[], ): string => { - if (price.recurringInterval !== 'year') { + if (price.recurringInterval !== SubscriptionInterval.Year) { return 'Cancel anytime'; } const monthPrice = prices.filter( - (price) => price.recurringInterval === 'month', + (price) => price.recurringInterval === SubscriptionInterval.Month, )?.[0]; if ( isDefined(monthPrice) && diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index 4d8eeb0db0f..a373b16a0f5 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -26,6 +26,7 @@ import { Section } from '@/ui/layout/section/components/Section'; import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { OnboardingStatus, + SubscriptionInterval, useBillingPortalSessionQuery, useUpdateBillingSubscriptionMutation, } from '~/generated/graphql'; @@ -40,21 +41,21 @@ const StyledInvisibleChat = styled.div` `; type SwitchInfo = { - newInterval: string; + newInterval: SubscriptionInterval; to: string; from: string; impact: string; }; const MONTHLY_SWITCH_INFO: SwitchInfo = { - newInterval: 'year', + newInterval: SubscriptionInterval.Year, to: 'to yearly', from: 'from monthly to yearly', impact: 'You will be charged immediately for the full year.', }; const YEARLY_SWITCH_INFO: SwitchInfo = { - newInterval: 'month', + newInterval: SubscriptionInterval.Month, to: 'to monthly', from: 'from yearly to monthly', impact: 'Your credit balance will be used to pay the monthly bills.', @@ -71,7 +72,8 @@ export const SettingsBilling = () => { const currentWorkspace = useRecoilValue(currentWorkspaceState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); const switchingInfo = - currentWorkspace?.currentBillingSubscription?.interval === 'year' + currentWorkspace?.currentBillingSubscription?.interval === + SubscriptionInterval.Year ? SWITCH_INFOS.year : SWITCH_INFOS.month; const [isSwitchingIntervalModalOpen, setIsSwitchingIntervalModalOpen] = diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index 4a2c296e98d..0df66e73b70 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -9,6 +9,7 @@ import { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.ser import { BillingSubscription, SubscriptionInterval, + SubscriptionStatus, } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { BillingSubscriptionItem } from 'src/engine/core-modules/billing/entities/billing-subscription-item.entity'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -87,7 +88,7 @@ export class BillingService { }) { const notCanceledSubscriptions = await this.billingSubscriptionRepository.find({ - where: { ...criteria, status: Not('canceled') }, + where: { ...criteria, status: Not(SubscriptionStatus.Canceled) }, relations: ['billingSubscriptionItems'], }); diff --git a/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts b/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts index 778f0671ee6..a64a1f7f041 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts @@ -3,9 +3,11 @@ import { ArgsType, Field } from '@nestjs/graphql'; import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; import Stripe from 'stripe'; +import { SubscriptionInterval } from '../entities/billing-subscription.entity'; + @ArgsType() export class CheckoutSessionInput { - @Field(() => String) + @Field(() => SubscriptionInterval) @IsString() @IsNotEmpty() recurringInterval: Stripe.Price.Recurring.Interval; diff --git a/packages/twenty-server/src/engine/core-modules/billing/dto/product-price.entity.ts b/packages/twenty-server/src/engine/core-modules/billing/dto/product-price.entity.ts index 69fc80011f3..d8c2d6d434f 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/dto/product-price.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/dto/product-price.entity.ts @@ -2,9 +2,11 @@ import { Field, ObjectType } from '@nestjs/graphql'; import Stripe from 'stripe'; +import { SubscriptionInterval } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; + @ObjectType() export class ProductPriceEntity { - @Field(() => String) + @Field(() => SubscriptionInterval) recurringInterval: Stripe.Price.Recurring.Interval; @Field(() => Number) From b8e2351255df9c932bd92665404e02fbf3eab2d3 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 10:14:52 +0200 Subject: [PATCH 28/47] Fix typing --- .../twenty-front/src/testing/mock-data/users.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/twenty-front/src/testing/mock-data/users.ts b/packages/twenty-front/src/testing/mock-data/users.ts index 9690d960628..a50ead14a19 100644 --- a/packages/twenty-front/src/testing/mock-data/users.ts +++ b/packages/twenty-front/src/testing/mock-data/users.ts @@ -1,5 +1,11 @@ import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; -import { OnboardingStatus, User, Workspace } from '~/generated/graphql'; +import { + OnboardingStatus, + SubscriptionInterval, + SubscriptionStatus, + User, + Workspace, +} from '~/generated/graphql'; type MockedUser = Pick< User, @@ -30,7 +36,7 @@ export const mockDefaultWorkspace: Workspace = { inviteHash: 'twenty.com-invite-hash', logo: workspaceLogoUrl, allowImpersonation: true, - subscriptionStatus: 'active', + subscriptionStatus: SubscriptionStatus.Active, activationStatus: 'active', featureFlags: [ { @@ -58,8 +64,8 @@ export const mockDefaultWorkspace: Workspace = { currentBillingSubscription: { __typename: 'BillingSubscription', id: '7efbc3f7-6e5e-4128-957e-8d86808cdf6a', - interval: 'month', - status: 'active', + interval: SubscriptionInterval.Month, + status: SubscriptionStatus.Active, }, }; From e2a8cfd7593ff7c144c991aa53f989c667e84edb Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 11:03:56 +0200 Subject: [PATCH 29/47] Fix import --- .../engine/core-modules/billing/dto/checkout-session.input.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts b/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts index a64a1f7f041..48738b85f8d 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/dto/checkout-session.input.ts @@ -3,7 +3,7 @@ import { ArgsType, Field } from '@nestjs/graphql'; import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; import Stripe from 'stripe'; -import { SubscriptionInterval } from '../entities/billing-subscription.entity'; +import { SubscriptionInterval } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; @ArgsType() export class CheckoutSessionInput { From 6fb040362e0929219b54469d82debc6cf44826f3 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 11:06:19 +0200 Subject: [PATCH 30/47] Fix tests --- .../__tests__/usePageChangeEffectNavigateLocation.test.ts | 3 ++- .../hooks/__tests__/useSetNextOnboardingStatus.test.ts | 4 ++-- .../ui/layout/hooks/__tests__/useShowAuthModal.test.tsx | 3 ++- .../src/engine/core-modules/billing/billing.service.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 59b0a097c0e..d05747f7960 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -295,7 +295,8 @@ describe('usePageChangeEffectNavigateLocation', () => { describe('tests should be exhaustive', () => { it('all location and onboarding status should be tested', () => { expect(testCases.length).toEqual( - Object.keys(AppPath).length * Object.keys(OnboardingStatus).length, + Object.keys(AppPath).length * + (Object.keys(OnboardingStatus).length + 1), ); }); }); diff --git a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts index 2e4db0a4ceb..23eff06a0e8 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts @@ -5,7 +5,7 @@ import { v4 } from 'uuid'; import { currentUserState } from '@/auth/states/currentUserState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus'; -import { OnboardingStatus } from '~/generated/graphql'; +import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { mockDefaultWorkspace, mockedUserData, @@ -34,7 +34,7 @@ const renderHooks = ( setCurrentWorkspace({ ...mockDefaultWorkspace, currentBillingSubscription: withCurrentBillingSubscription - ? { id: v4(), status: 'status' } + ? { id: v4(), status: SubscriptionStatus.Active } : undefined, }); useSetNextOnboardingStatus()(); diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index 747b36fd437..805fb2c3d80 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -326,7 +326,8 @@ describe('useShowAuthModal', () => { describe('tests should be exhaustive', () => { it('all location and onboarding status should be tested', () => { expect(testCases.length).toEqual( - Object.keys(AppPath).length * Object.keys(OnboardingStatus).length, + Object.keys(AppPath).length * + (Object.keys(OnboardingStatus).length + 1), ); }); }); diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index 0df66e73b70..f01bb83cafe 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -94,7 +94,7 @@ export class BillingService { assert( notCanceledSubscriptions.length <= 1, - `More than on not canceled subscription for workspace ${criteria.workspaceId}`, + `More than one not canceled subscription for workspace ${criteria.workspaceId}`, ); return notCanceledSubscriptions?.[0]; From 1e90fbdd65b55f331b04ddbdff840342492f2a26 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 11:37:59 +0200 Subject: [PATCH 31/47] Add new onboarding step --- .../twenty-front/src/generated/graphql.tsx | 2 +- ...sePageChangeEffectNavigateLocation.test.ts | 42 +++++++++---------- .../usePageChangeEffectNavigateLocation.ts | 2 +- .../hooks/__tests__/useShowAuthModal.test.tsx | 42 +++++++++---------- .../ui/layout/hooks/useShowAuthModal.ts | 2 +- .../__stories__/ChooseYourPlan.stories.tsx | 2 +- .../enums/onboarding-status.enum.ts | 4 +- .../onboarding/onboarding.service.ts | 2 +- 8 files changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index c4c0ad5629c..dcca8597038 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -466,9 +466,9 @@ export enum OnboardingStatus { Completed = 'COMPLETED', CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', + PlanRequired = 'PLAN_REQUIRED', ProfileCreation = 'PROFILE_CREATION', SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', - SubscriptionIncomplete = 'SUBSCRIPTION_INCOMPLETE', SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index d05747f7960..3a0dca5a62d 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -30,7 +30,7 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({ // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Verify, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -42,7 +42,7 @@ const testCases = [ { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SignInUp, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -54,7 +54,7 @@ const testCases = [ { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.Invite, status: OnboardingStatus.PlanRequired, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -66,7 +66,7 @@ const testCases = [ { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.PlanRequired, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -78,7 +78,7 @@ const testCases = [ { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -90,7 +90,7 @@ const testCases = [ { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -102,7 +102,7 @@ const testCases = [ { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -114,7 +114,7 @@ const testCases = [ { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -126,7 +126,7 @@ const testCases = [ { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionIncomplete, res: undefined }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.PlanRequired, res: undefined }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -138,7 +138,7 @@ const testCases = [ { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -150,7 +150,7 @@ const testCases = [ { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Index, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, @@ -162,7 +162,7 @@ const testCases = [ { loc: AppPath.Index, status: OnboardingStatus.Completed, res: defaultHomePagePath }, { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.TasksPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -174,7 +174,7 @@ const testCases = [ { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -186,7 +186,7 @@ const testCases = [ { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -198,7 +198,7 @@ const testCases = [ { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -210,7 +210,7 @@ const testCases = [ { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -222,7 +222,7 @@ const testCases = [ { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -234,7 +234,7 @@ const testCases = [ { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Impersonate, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -246,7 +246,7 @@ const testCases = [ { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.Authorize, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -258,7 +258,7 @@ const testCases = [ { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, @@ -270,7 +270,7 @@ const testCases = [ { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: undefined }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionIncomplete, res: AppPath.PlanRequired }, + { loc: AppPath.NotFound, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index 9a7e371b835..93dd6d6df6e 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -37,7 +37,7 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStatus.SubscriptionIncomplete && + onboardingStatus === OnboardingStatus.PlanRequired && !isMatchingLocation(AppPath.PlanRequired) ) { return AppPath.PlanRequired; diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index 805fb2c3d80..ba4a5266e1f 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -41,7 +41,7 @@ const getResult = (isDefaultLayoutAuthModalVisible = true) => // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionIncomplete, res: false }, + { loc: AppPath.Verify, status: OnboardingStatus.PlanRequired, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -53,7 +53,7 @@ const testCases = [ { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.SignInUp, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -65,7 +65,7 @@ const testCases = [ { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Invite, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: true }, @@ -77,7 +77,7 @@ const testCases = [ { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: true }, { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.ResetPassword, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: true }, @@ -89,7 +89,7 @@ const testCases = [ { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: true }, { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.CreateWorkspace, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -101,7 +101,7 @@ const testCases = [ { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.CreateProfile, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -113,7 +113,7 @@ const testCases = [ { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.SyncEmails, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -125,7 +125,7 @@ const testCases = [ { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.InviteTeam, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -137,7 +137,7 @@ const testCases = [ { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.PlanRequired, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: true }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -149,7 +149,7 @@ const testCases = [ { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -161,7 +161,7 @@ const testCases = [ { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Index, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -173,7 +173,7 @@ const testCases = [ { loc: AppPath.Index, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.TasksPage, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -185,7 +185,7 @@ const testCases = [ { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -197,7 +197,7 @@ const testCases = [ { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.RecordIndexPage, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -209,7 +209,7 @@ const testCases = [ { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.RecordShowPage, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -221,7 +221,7 @@ const testCases = [ { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -233,7 +233,7 @@ const testCases = [ { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -245,7 +245,7 @@ const testCases = [ { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Impersonate, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -257,7 +257,7 @@ const testCases = [ { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.Authorize, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -269,7 +269,7 @@ const testCases = [ { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: false }, @@ -281,7 +281,7 @@ const testCases = [ { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: false }, { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionIncomplete, res: true }, + { loc: AppPath.NotFound, status: OnboardingStatus.PlanRequired, res: true }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: false }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: false }, { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: false }, diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index 2c72fd59d0c..b06c051197c 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -25,7 +25,7 @@ export const useShowAuthModal = () => { } if ( !onboardingStatus || - onboardingStatus === OnboardingStatus.SubscriptionIncomplete || + onboardingStatus === OnboardingStatus.PlanRequired || onboardingStatus === OnboardingStatus.ProfileCreation || onboardingStatus === OnboardingStatus.WorkspaceActivation || onboardingStatus === OnboardingStatus.SyncEmail || diff --git a/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx b/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx index 3370984ddfc..b42177fedd0 100644 --- a/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx +++ b/packages/twenty-front/src/pages/onboarding/__stories__/ChooseYourPlan.stories.tsx @@ -27,7 +27,7 @@ const meta: Meta = { return HttpResponse.json({ data: { currentUser: mockedOnboardingUserData( - OnboardingStatus.SubscriptionIncomplete, + OnboardingStatus.PlanRequired, ), }, }); diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts index 1086f9a31df..7b5d380c46a 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts @@ -1,12 +1,12 @@ export enum OnboardingStatus { - SUBSCRIPTION_INCOMPLETE = 'SUBSCRIPTION_INCOMPLETE', SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED', SUBSCRIPTION_PAST_DUE = 'SUBSCRIPTION_PAST_DUE', SUBSCRIPTION_UNPAID = 'SUBSCRIPTION_UNPAID', WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', + COMPLETED_WITHOUT_SUBSCRIPTION = 'COMPLETED_WITHOUT_SUBSCRIPTION', + PLAN_REQUIRED = 'PLAN_REQUIRED', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', INVITE_TEAM = 'INVITE_TEAM', - COMPLETED_WITHOUT_SUBSCRIPTION = 'COMPLETED_WITHOUT_SUBSCRIPTION', COMPLETED = 'COMPLETED', } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index d5eb9ebbcf8..affc5dd2575 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -135,7 +135,7 @@ export class OnboardingService { async getOnboardingStatus(user: User) { if (await this.isSubscriptionIncompleteOnboardingStatus(user)) { - return OnboardingStatus.SUBSCRIPTION_INCOMPLETE; + return OnboardingStatus.PLAN_REQUIRED; } if (await this.isWorkspaceActivationOnboardingStatus(user)) { From 602fcef5cbc38c49a5bbd664e52d1ff991bf3ade Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 11:43:49 +0200 Subject: [PATCH 32/47] Fix useSetNextOnboardingStatus --- .../hooks/__tests__/useSetNextOnboardingStatus.test.ts | 2 +- .../src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts index 23eff06a0e8..86f90483170 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts @@ -26,7 +26,7 @@ const renderHooks = ( '@/object-record/hooks/useFindManyRecords', ); useFindManyRecordsMock.useFindManyRecords.mockReturnValue({ - records: withManyWorkspaceMembers ? [{}] : [], + records: withManyWorkspaceMembers ? [{}, {}] : [{}], }); const setCurrentUser = useSetRecoilState(currentUserState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index be7b6709830..5109a943cd0 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -21,7 +21,7 @@ const getNextOnboardingStatus = ( } if ( currentUser?.onboardingStatus === OnboardingStatus.SyncEmail && - workspaceMembers?.length === 0 + workspaceMembers?.length <= 1 ) { return OnboardingStatus.InviteTeam; } From 66f4277ed9206821f359784e1d5628b7f86e0e80 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 13:58:34 +0200 Subject: [PATCH 33/47] Remove subscription onboarding status --- .../src/generated/graphql.tsx | 34 +- .../src/generated-metadata/graphql.ts | 34 +- .../twenty-front/src/generated/graphql.tsx | 4 - ...sePageChangeEffectNavigateLocation.test.ts | 479 ++++++++-------- .../usePageChangeEffectNavigateLocation.ts | 21 +- .../__tests__}/useOnboardingStatus.test.ts | 2 +- .../useSetNextOnboardingStatus.test.ts | 7 - .../hooks/useOnboardingStatus.ts | 0 .../hooks/useSetNextOnboardingStatus.ts | 4 +- .../hooks/__tests__/useShowAuthModal.test.tsx | 523 +++++++++--------- .../ui/layout/hooks/useShowAuthModal.ts | 21 +- .../__tests__/useSubscriptionStatus.test.ts | 86 +++ .../workspace/hooks/useSubscriptionStatus.ts | 22 + .../hooks/useWorkspaceHasSubscription.ts | 10 + .../src/pages/onboarding/CreateProfile.tsx | 2 +- .../src/pages/onboarding/CreateWorkspace.tsx | 2 +- .../src/pages/settings/SettingsBilling.tsx | 16 +- .../enums/onboarding-status.enum.ts | 6 +- .../onboarding/onboarding.service.ts | 49 -- 19 files changed, 719 insertions(+), 603 deletions(-) rename packages/twenty-front/src/modules/{auth/hooks/__test__ => onboarding/hooks/__tests__}/useOnboardingStatus.test.ts (95%) rename packages/twenty-front/src/modules/{auth => onboarding}/hooks/useOnboardingStatus.ts (100%) create mode 100644 packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts create mode 100644 packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts create mode 100644 packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts diff --git a/packages/twenty-chrome-extension/src/generated/graphql.tsx b/packages/twenty-chrome-extension/src/generated/graphql.tsx index a1a13672ff4..d48802b3177 100644 --- a/packages/twenty-chrome-extension/src/generated/graphql.tsx +++ b/packages/twenty-chrome-extension/src/generated/graphql.tsx @@ -853,8 +853,8 @@ export type Billing = { export type BillingSubscription = { id: Scalars['UUID']; - interval?: Maybe; - status: Scalars['String']; + interval?: Maybe; + status: SubscriptionStatus; }; export type BillingSubscriptionFilter = { @@ -3725,7 +3725,7 @@ export type MutationChallengeArgs = { export type MutationCheckoutSessionArgs = { - recurringInterval: Scalars['String']; + recurringInterval: SubscriptionInterval; successUrlPath?: InputMaybe; }; @@ -4882,13 +4882,9 @@ export type ObjectFieldsConnection = { /** Onboarding status */ export enum OnboardingStatus { Completed = 'COMPLETED', - CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', + PlanRequired = 'PLAN_REQUIRED', ProfileCreation = 'PROFILE_CREATION', - SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', - SubscriptionIncomplete = 'SUBSCRIPTION_INCOMPLETE', - SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', - SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } @@ -5425,7 +5421,7 @@ export type PostgresCredentials = { export type ProductPriceEntity = { created: Scalars['Float']; - recurringInterval: Scalars['String']; + recurringInterval: SubscriptionInterval; stripePriceId: Scalars['String']; unitAmount: Scalars['Float']; }; @@ -6318,6 +6314,24 @@ export type StringFilter = { startsWith?: InputMaybe; }; +export enum SubscriptionInterval { + Day = 'Day', + Month = 'Month', + Week = 'Week', + Year = 'Year' +} + +export enum SubscriptionStatus { + Active = 'Active', + Canceled = 'Canceled', + Incomplete = 'Incomplete', + IncompleteExpired = 'IncompleteExpired', + PastDue = 'PastDue', + Paused = 'Paused', + Trialing = 'Trialing', + Unpaid = 'Unpaid' +} + export type Support = { supportDriver: Scalars['String']; supportFrontChatId?: Maybe; @@ -7313,7 +7327,7 @@ export type Workspace = { id: Scalars['UUID']; inviteHash?: Maybe; logo?: Maybe; - subscriptionStatus: Scalars['String']; + subscriptionStatus: SubscriptionStatus; updatedAt: Scalars['DateTime']; }; diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 2a9c888689c..778702cb208 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -98,8 +98,8 @@ export type Billing = { export type BillingSubscription = { __typename?: 'BillingSubscription'; id: Scalars['UUID']['output']; - interval?: Maybe; - status: Scalars['String']['output']; + interval?: Maybe; + status: SubscriptionStatus; }; export type BillingSubscriptionFilter = { @@ -453,7 +453,7 @@ export type MutationChallengeArgs = { export type MutationCheckoutSessionArgs = { - recurringInterval: Scalars['String']['input']; + recurringInterval: SubscriptionInterval; successUrlPath?: InputMaybe; }; @@ -641,13 +641,9 @@ export type ObjectFieldsConnection = { /** Onboarding status */ export enum OnboardingStatus { Completed = 'COMPLETED', - CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', + PlanRequired = 'PLAN_REQUIRED', ProfileCreation = 'PROFILE_CREATION', - SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', - SubscriptionIncomplete = 'SUBSCRIPTION_INCOMPLETE', - SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', - SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } @@ -681,7 +677,7 @@ export type PostgresCredentials = { export type ProductPriceEntity = { __typename?: 'ProductPriceEntity'; created: Scalars['Float']['output']; - recurringInterval: Scalars['String']['output']; + recurringInterval: SubscriptionInterval; stripePriceId: Scalars['String']['output']; unitAmount: Scalars['Float']['output']; }; @@ -931,6 +927,24 @@ export enum SortNulls { NullsLast = 'NULLS_LAST' } +export enum SubscriptionInterval { + Day = 'Day', + Month = 'Month', + Week = 'Week', + Year = 'Year' +} + +export enum SubscriptionStatus { + Active = 'Active', + Canceled = 'Canceled', + Incomplete = 'Incomplete', + IncompleteExpired = 'IncompleteExpired', + PastDue = 'PastDue', + Paused = 'Paused', + Trialing = 'Trialing', + Unpaid = 'Unpaid' +} + export type Support = { __typename?: 'Support'; supportDriver: Scalars['String']['output']; @@ -1182,7 +1196,7 @@ export type Workspace = { id: Scalars['UUID']['output']; inviteHash?: Maybe; logo?: Maybe; - subscriptionStatus: Scalars['String']['output']; + subscriptionStatus: SubscriptionStatus; updatedAt: Scalars['DateTime']['output']; }; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index dcca8597038..0292ef8a4fb 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -464,13 +464,9 @@ export type ObjectFieldsConnection = { /** Onboarding status */ export enum OnboardingStatus { Completed = 'COMPLETED', - CompletedWithoutSubscription = 'COMPLETED_WITHOUT_SUBSCRIPTION', InviteTeam = 'INVITE_TEAM', PlanRequired = 'PLAN_REQUIRED', ProfileCreation = 'PROFILE_CREATION', - SubscriptionCanceled = 'SUBSCRIPTION_CANCELED', - SubscriptionPastDue = 'SUBSCRIPTION_PAST_DUE', - SubscriptionUnpaid = 'SUBSCRIPTION_UNPAID', SyncEmail = 'SYNC_EMAIL', WorkspaceActivation = 'WORKSPACE_ACTIVATION' } diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 3a0dca5a62d..c42ce0d13dd 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -1,17 +1,33 @@ -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; -import { OnboardingStatus } from '~/generated/graphql'; +import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; +import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; +import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation'; -jest.mock('@/auth/hooks/useOnboardingStatus'); +jest.mock('@/onboarding/hooks/useOnboardingStatus'); const setupMockOnboardingStatus = ( onboardingStatus: OnboardingStatus | undefined, ) => { jest.mocked(useOnboardingStatus).mockReturnValueOnce(onboardingStatus); }; +jest.mock('@/workspace/hooks/useSubscriptionStatus'); +const setupMockSubscriptionStatus = ( + subscriptionStatus: SubscriptionStatus | undefined, +) => { + jest.mocked(useSubscriptionStatus).mockReturnValueOnce(subscriptionStatus); +}; + +jest.mock('@/workspace/hooks/useWorkspaceHasSubscription'); +const setupMockWorkspaceHasSubscription = (workspaceHasSubscription = true) => { + jest + .mocked(useWorkspaceHasSubscription) + .mockReturnValueOnce(workspaceHasSubscription); +}; + jest.mock('~/hooks/useIsMatchingLocation'); const mockUseIsMatchingLocation = jest.mocked(useIsMatchingLocation); @@ -30,273 +46,262 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({ // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.Verify, status: undefined, res: undefined }, - { loc: AppPath.Verify, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Verify, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Verify, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Verify, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: undefined, res: undefined }, - { loc: AppPath.SignInUp, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SignInUp, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SignInUp, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Invite, status: OnboardingStatus.PlanRequired, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Invite, status: undefined, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.ProfileCreation, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.SyncEmail, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.InviteTeam, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.PlanRequired, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.ResetPassword, status: undefined, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.ProfileCreation, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SyncEmail, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.InviteTeam, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.CreateWorkspace, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.ProfileCreation, res: undefined }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SyncEmail, res: undefined }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.InviteTeam, res: undefined }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.PlanRequired, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: defaultHomePagePath }, - { loc: AppPath.Index, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.Index, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Index, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Index, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Index, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Index, status: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: defaultHomePagePath }, + { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.TasksPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.TasksPage, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.TasksPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.TasksPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.TasksPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.RecordIndexPage, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.RecordShowPage, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Impersonate, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.Impersonate, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Impersonate, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Impersonate, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.Authorize, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.Authorize, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Authorize, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Authorize, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Authorize, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: '/settings/billing' }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: '/settings/billing' }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: undefined }, - { loc: AppPath.NotFound, status: undefined, res: AppPath.SignInUp }, - { loc: AppPath.NotFound, status: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFound, status: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFound, status: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFound, status: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFound, status: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFound, status: OnboardingStatus.CompletedWithoutSubscription, res: undefined }, + { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, ]; describe('usePageChangeEffectNavigateLocation', () => { testCases.forEach((testCase) => { - it(`with location ${testCase.loc} and onboardingStatus ${testCase.status} should return ${testCase.res}`, () => { + it(`with location ${testCase.loc} and onboardingStatus ${testCase.onboardingStatus} and subscriptionStatus ${testCase.subscriptionStatus} should return ${testCase.res}`, () => { setupMockIsMatchingLocation(testCase.loc); - setupMockOnboardingStatus(testCase.status); + setupMockOnboardingStatus(testCase.onboardingStatus); + setupMockSubscriptionStatus(testCase.subscriptionStatus); + setupMockWorkspaceHasSubscription(); expect(usePageChangeEffectNavigateLocation()).toEqual(testCase.res); }); }); describe('tests should be exhaustive', () => { it('all location and onboarding status should be tested', () => { + const untestedSubscriptionStatus = [ + SubscriptionStatus.Active, + SubscriptionStatus.IncompleteExpired, + SubscriptionStatus.Paused, + SubscriptionStatus.Trialing, + ]; expect(testCases.length).toEqual( Object.keys(AppPath).length * - (Object.keys(OnboardingStatus).length + 1), + (Object.keys(OnboardingStatus).length + + (Object.keys(SubscriptionStatus).length - + untestedSubscriptionStatus.length)), ); }); }); diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index 93dd6d6df6e..00f0ad075f3 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -1,13 +1,17 @@ -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; -import { OnboardingStatus } from '~/generated/graphql'; +import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; +import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; +import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; export const usePageChangeEffectNavigateLocation = () => { const isMatchingLocation = useIsMatchingLocation(); const onboardingStatus = useOnboardingStatus(); + const subscriptionStatus = useSubscriptionStatus(); + const workspaceHasSubscription = useWorkspaceHasSubscription(); const { defaultHomePagePath } = useDefaultHomePagePath(); const isMatchingOpenRoute = @@ -44,7 +48,7 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - onboardingStatus === OnboardingStatus.SubscriptionUnpaid && + subscriptionStatus === SubscriptionStatus.Unpaid && !isMatchingLocation(AppPath.SettingsCatchAll) ) { return `${AppPath.SettingsCatchAll.replace('/*', '')}/${ @@ -53,8 +57,7 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - (onboardingStatus === OnboardingStatus.SubscriptionUnpaid || - onboardingStatus === OnboardingStatus.SubscriptionCanceled) && + subscriptionStatus === SubscriptionStatus.Canceled && !( isMatchingLocation(AppPath.SettingsCatchAll) || isMatchingLocation(AppPath.PlanRequired) @@ -95,15 +98,17 @@ export const usePageChangeEffectNavigateLocation = () => { } if ( - (onboardingStatus === OnboardingStatus.Completed || - onboardingStatus === OnboardingStatus.SubscriptionPastDue) && + onboardingStatus === OnboardingStatus.Completed && + workspaceHasSubscription && + subscriptionStatus !== SubscriptionStatus.Canceled && isMatchingOnboardingRoute ) { return defaultHomePagePath; } if ( - onboardingStatus === OnboardingStatus.CompletedWithoutSubscription && + onboardingStatus === OnboardingStatus.Completed && + !workspaceHasSubscription && isMatchingOnboardingRoute && !isMatchingLocation(AppPath.PlanRequired) ) { diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useOnboardingStatus.test.ts similarity index 95% rename from packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts rename to packages/twenty-front/src/modules/onboarding/hooks/__tests__/useOnboardingStatus.test.ts index b0bf5b4d3a1..3aaf6449502 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useOnboardingStatus.test.ts @@ -2,9 +2,9 @@ import { act } from 'react-dom/test-utils'; import { renderHook } from '@testing-library/react'; import { RecoilRoot, useSetRecoilState } from 'recoil'; -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { CurrentUser, currentUserState } from '@/auth/states/currentUserState'; import { tokenPairState } from '@/auth/states/tokenPairState'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { OnboardingStatus } from '~/generated/graphql'; const tokenPair = { diff --git a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts index 86f90483170..85b6b631af4 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts @@ -67,11 +67,4 @@ describe('useSetNextOnboardingStatus', () => { const result = renderHooks(OnboardingStatus.InviteTeam, true, false); expect(result.current).toEqual(OnboardingStatus.Completed); }); - - it('should set next onboarding status for Completed without subscription', () => { - const result = renderHooks(OnboardingStatus.InviteTeam, false, false); - expect(result.current).toEqual( - OnboardingStatus.CompletedWithoutSubscription, - ); - }); }); diff --git a/packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useOnboardingStatus.ts similarity index 100% rename from packages/twenty-front/src/modules/auth/hooks/useOnboardingStatus.ts rename to packages/twenty-front/src/modules/onboarding/hooks/useOnboardingStatus.ts diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index 5109a943cd0..086e0d713c7 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -25,9 +25,7 @@ const getNextOnboardingStatus = ( ) { return OnboardingStatus.InviteTeam; } - return currentWorkspace?.currentBillingSubscription - ? OnboardingStatus.Completed - : OnboardingStatus.CompletedWithoutSubscription; + return OnboardingStatus.Completed; }; export const useSetNextOnboardingStatus = () => { diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index ba4a5266e1f..780b26dd670 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -1,20 +1,36 @@ import { renderHook } from '@testing-library/react'; import { RecoilRoot, useSetRecoilState } from 'recoil'; -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; -import { OnboardingStatus } from '~/generated/graphql'; +import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; +import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; +import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; -jest.mock('@/auth/hooks/useOnboardingStatus'); +jest.mock('@/onboarding/hooks/useOnboardingStatus'); const setupMockOnboardingStatus = ( onboardingStatus: OnboardingStatus | undefined, ) => { jest.mocked(useOnboardingStatus).mockReturnValueOnce(onboardingStatus); }; +jest.mock('@/workspace/hooks/useSubscriptionStatus'); +const setupMockSubscriptionStatus = ( + subscriptionStatus: SubscriptionStatus | undefined, +) => { + jest.mocked(useSubscriptionStatus).mockReturnValueOnce(subscriptionStatus); +}; + +jest.mock('@/workspace/hooks/useWorkspaceHasSubscription'); +const setupMockWorkspaceHasSubscription = (workspaceHasSubscription = true) => { + jest + .mocked(useWorkspaceHasSubscription) + .mockReturnValueOnce(workspaceHasSubscription); +}; + jest.mock('~/hooks/useIsMatchingLocation'); const mockUseIsMatchingLocation = jest.mocked(useIsMatchingLocation); @@ -41,264 +57,245 @@ const getResult = (isDefaultLayoutAuthModalVisible = true) => // prettier-ignore const testCases = [ - { loc: AppPath.Verify, status: OnboardingStatus.PlanRequired, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Verify, status: undefined, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.WorkspaceActivation, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.ProfileCreation, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.SyncEmail, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.InviteTeam, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Verify, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.SignInUp, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.SignInUp, status: undefined, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.SignInUp, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SignInUp, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.Invite, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionCanceled, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionUnpaid, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.SubscriptionPastDue, res: true }, - { loc: AppPath.Invite, status: undefined, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.Completed, res: true }, - { loc: AppPath.Invite, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - - { loc: AppPath.ResetPassword, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionCanceled, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionUnpaid, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SubscriptionPastDue, res: true }, - { loc: AppPath.ResetPassword, status: undefined, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.Completed, res: true }, - { loc: AppPath.ResetPassword, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.CreateWorkspace, status: undefined, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateWorkspace, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.CreateProfile, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.CreateProfile, status: undefined, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateProfile, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.SyncEmails, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.SyncEmails, status: undefined, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SyncEmails, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.InviteTeam, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.InviteTeam, status: undefined, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.InviteTeam, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.PlanRequired, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionCanceled, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.PlanRequired, status: undefined, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequired, status: OnboardingStatus.CompletedWithoutSubscription, res: true }, - - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: undefined, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequiredSuccess, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.Index, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Index, status: undefined, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Index, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Index, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.TasksPage, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.TasksPage, status: undefined, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.TasksPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.TasksPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.OpportunitiesPage, status: undefined, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.OpportunitiesPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.RecordIndexPage, status: undefined, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordIndexPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.RecordShowPage, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.RecordShowPage, status: undefined, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordShowPage, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.SettingsCatchAll, status: undefined, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SettingsCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.DevelopersCatchAll, status: undefined, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.DevelopersCatchAll, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.Impersonate, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Impersonate, status: undefined, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Impersonate, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Impersonate, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.Authorize, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.Authorize, status: undefined, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Authorize, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Authorize, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.NotFoundWildcard, status: undefined, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFoundWildcard, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, - - { loc: AppPath.NotFound, status: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionCanceled, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionUnpaid, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.SubscriptionPastDue, res: false }, - { loc: AppPath.NotFound, status: undefined, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.NotFound, status: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFound, status: OnboardingStatus.CompletedWithoutSubscription, res: false }, + { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: undefined, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: false }, + { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: true }, + + { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: true }, + + { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, ]; describe('useShowAuthModal', () => { testCases.forEach((testCase) => { - it(`testCase for location ${testCase.loc} with onboardingStatus ${testCase.status} should return ${testCase.res}`, () => { - setupMockOnboardingStatus(testCase.status); + it(`testCase for location ${testCase.loc} with onboardingStatus ${testCase.onboardingStatus} should return ${testCase.res}`, () => { + setupMockOnboardingStatus(testCase.onboardingStatus); + setupMockSubscriptionStatus(testCase.subscriptionStatus); setupMockIsMatchingLocation(testCase.loc); + setupMockWorkspaceHasSubscription(); const { result } = getResult(); if (testCase.res) { expect(result.current).toBeTruthy(); @@ -311,13 +308,17 @@ describe('useShowAuthModal', () => { describe('test with token validation loading', () => { it(`with appPath ${AppPath.Invite} and isDefaultLayoutAuthModalVisible=false`, () => { setupMockOnboardingStatus(OnboardingStatus.Completed); + setupMockSubscriptionStatus(SubscriptionStatus.Active); setupMockIsMatchingLocation(AppPath.Invite); + setupMockWorkspaceHasSubscription(); const { result } = getResult(false); expect(result.current).toBeFalsy(); }); it(`with appPath ${AppPath.ResetPassword} and isDefaultLayoutAuthModalVisible=false`, () => { setupMockOnboardingStatus(OnboardingStatus.Completed); + setupMockSubscriptionStatus(SubscriptionStatus.Active); setupMockIsMatchingLocation(AppPath.ResetPassword); + setupMockWorkspaceHasSubscription(); const { result } = getResult(false); expect(result.current).toBeFalsy(); }); @@ -325,9 +326,17 @@ describe('useShowAuthModal', () => { describe('tests should be exhaustive', () => { it('all location and onboarding status should be tested', () => { + const untestedSubscriptionStatus = [ + SubscriptionStatus.Active, + SubscriptionStatus.IncompleteExpired, + SubscriptionStatus.Paused, + SubscriptionStatus.Trialing, + ]; expect(testCases.length).toEqual( Object.keys(AppPath).length * - (Object.keys(OnboardingStatus).length + 1), + (Object.keys(OnboardingStatus).length + + (Object.keys(SubscriptionStatus).length - + untestedSubscriptionStatus.length)), ); }); }); diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index b06c051197c..be4ea591936 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -1,15 +1,19 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; -import { OnboardingStatus } from '~/generated/graphql'; +import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; +import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; +import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; export const useShowAuthModal = () => { const isMatchingLocation = useIsMatchingLocation(); const onboardingStatus = useOnboardingStatus(); + const subscriptionStatus = useSubscriptionStatus(); + const workspaceHasSubscription = useWorkspaceHasSubscription(); const isDefaultLayoutAuthModalVisible = useRecoilValue( isDefaultLayoutAuthModalVisibleState, ); @@ -35,10 +39,17 @@ export const useShowAuthModal = () => { } if (isMatchingLocation(AppPath.PlanRequired)) { return ( - onboardingStatus === OnboardingStatus.CompletedWithoutSubscription || - onboardingStatus === OnboardingStatus.SubscriptionCanceled + (onboardingStatus === OnboardingStatus.Completed && + !workspaceHasSubscription) || + subscriptionStatus === SubscriptionStatus.Canceled ); } return false; - }, [isDefaultLayoutAuthModalVisible, isMatchingLocation, onboardingStatus]); + }, [ + isDefaultLayoutAuthModalVisible, + isMatchingLocation, + workspaceHasSubscription, + onboardingStatus, + subscriptionStatus, + ]); }; diff --git a/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts b/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts new file mode 100644 index 00000000000..d6cb6d3116e --- /dev/null +++ b/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts @@ -0,0 +1,86 @@ +import { act } from 'react-dom/test-utils'; +import { renderHook } from '@testing-library/react'; +import { RecoilRoot, useSetRecoilState } from 'recoil'; + +import { + CurrentWorkspace, + currentWorkspaceState, +} from '@/auth/states/currentWorkspaceState'; +import { tokenPairState } from '@/auth/states/tokenPairState'; +import { billingState } from '@/client-config/states/billingState'; +import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; +import { SubscriptionStatus } from '~/generated/graphql'; + +const tokenPair = { + accessToken: { token: 'accessToken', expiresAt: 'expiresAt' }, + refreshToken: { token: 'refreshToken', expiresAt: 'expiresAt' }, +}; +const currentWorkspace = { + id: '1', + subscriptionStatus: SubscriptionStatus.Incomplete, + activationStatus: 'active', + allowImpersonation: true, +} as CurrentWorkspace; + +const renderHooks = () => { + const { result } = renderHook( + () => { + const subscriptionStatus = useSubscriptionStatus(); + const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); + const setTokenPair = useSetRecoilState(tokenPairState); + const setBilling = useSetRecoilState(billingState); + + return { + subscriptionStatus, + setCurrentWorkspace, + setTokenPair, + setBilling, + }; + }, + { + wrapper: RecoilRoot, + }, + ); + return { result }; +}; + +describe('useSubscriptionStatus', () => { + it(`should return "undefined" when user is not logged in`, async () => { + const { result } = renderHooks(); + expect(result.current.subscriptionStatus).toBe(undefined); + }); + + Object.values(SubscriptionStatus).forEach((subscriptionStatus) => { + it(`should return "active" when billing not enabled`, async () => { + const { result } = renderHooks(); + const { setTokenPair, setCurrentWorkspace, setBilling } = result.current; + act(() => { + setBilling({ isBillingEnabled: false }); + setTokenPair(tokenPair); + setCurrentWorkspace({ + ...currentWorkspace, + subscriptionStatus, + }); + }); + expect(result.current.subscriptionStatus).toBe(SubscriptionStatus.Active); + }); + }); + + Object.values(SubscriptionStatus).forEach((subscriptionStatus) => { + it(`should return "${subscriptionStatus}" when billing enabled`, async () => { + const { result } = renderHooks(); + const { setTokenPair, setCurrentWorkspace, setBilling } = result.current; + + act(() => { + setBilling({ isBillingEnabled: true }); + setTokenPair(tokenPair); + setCurrentWorkspace({ + ...currentWorkspace, + subscriptionStatus, + }); + }); + + expect(result.current.subscriptionStatus).toBe(subscriptionStatus); + }); + }); +}); diff --git a/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts b/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts new file mode 100644 index 00000000000..7c11f5990d0 --- /dev/null +++ b/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts @@ -0,0 +1,22 @@ +import { useRecoilValue } from 'recoil'; + +import { useIsLogged } from '@/auth/hooks/useIsLogged'; +import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { billingState } from '@/client-config/states/billingState'; +import { SubscriptionStatus } from '~/generated/graphql'; + +export const useSubscriptionStatus = (): + | SubscriptionStatus + | null + | undefined => { + const currentWorkspace = useRecoilValue(currentWorkspaceState); + const billing = useRecoilValue(billingState); + const isLoggedIn = useIsLogged(); + if (!isLoggedIn) { + return undefined; + } + if (!billing?.isBillingEnabled) { + return SubscriptionStatus.Active; + } + return currentWorkspace?.subscriptionStatus; +}; diff --git a/packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts b/packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts new file mode 100644 index 00000000000..7f0c4733083 --- /dev/null +++ b/packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts @@ -0,0 +1,10 @@ +import { useRecoilValue } from 'recoil'; + +import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; +import { isDefined } from '~/utils/isDefined'; + +export const useWorkspaceHasSubscription = () => { + const currentWorkspace = useRecoilValue(currentWorkspaceState); + + return isDefined(currentWorkspace?.currentBillingSubscription); +}; diff --git a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx index 49c7286be5b..f5faa4ff4b1 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateProfile.tsx @@ -9,10 +9,10 @@ import { z } from 'zod'; import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { useSetNextOnboardingStatus } from '@/onboarding/hooks/useSetNextOnboardingStatus'; import { ProfilePictureUploader } from '@/settings/profile/components/ProfilePictureUploader'; import { PageHotkeyScope } from '@/types/PageHotkeyScope'; diff --git a/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx b/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx index b14b02ebbfe..b703ef13b0f 100644 --- a/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx +++ b/packages/twenty-front/src/pages/onboarding/CreateWorkspace.tsx @@ -9,10 +9,10 @@ import { z } from 'zod'; import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState'; import { FIND_MANY_OBJECT_METADATA_ITEMS } from '@/object-metadata/graphql/queries'; import { useApolloMetadataClient } from '@/object-metadata/hooks/useApolloMetadataClient'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { WorkspaceLogoUploader } from '@/settings/workspace/components/WorkspaceLogoUploader'; import { Loader } from '@/ui/feedback/loader/components/Loader'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index a373b16a0f5..916e055edcb 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -10,9 +10,9 @@ import { IconCurrencyDollar, } from 'twenty-ui'; -import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage'; +import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SupportChat } from '@/support/components/SupportChat'; import { AppPath } from '@/types/AppPath'; @@ -24,9 +24,12 @@ import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModa import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; +import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; +import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; import { OnboardingStatus, SubscriptionInterval, + SubscriptionStatus, useBillingPortalSessionQuery, useUpdateBillingSubscriptionMutation, } from '~/generated/graphql'; @@ -69,6 +72,8 @@ const SWITCH_INFOS = { export const SettingsBilling = () => { const { enqueueSnackBar } = useSnackBar(); const onboardingStatus = useOnboardingStatus(); + const subscriptionStatus = useSubscriptionStatus(); + const workspaceHasSubscription = useWorkspaceHasSubscription(); const currentWorkspace = useRecoilValue(currentWorkspaceState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); const switchingInfo = @@ -96,14 +101,15 @@ export const SettingsBilling = () => { onboardingStatus !== OnboardingStatus.Completed; const displayPaymentFailInfo = - onboardingStatus === OnboardingStatus.SubscriptionPastDue || - onboardingStatus === OnboardingStatus.SubscriptionUnpaid; + subscriptionStatus === SubscriptionStatus.PastDue || + subscriptionStatus === SubscriptionStatus.Unpaid; const displaySubscriptionCanceledInfo = - onboardingStatus === OnboardingStatus.SubscriptionCanceled; + subscriptionStatus === SubscriptionStatus.Canceled; const displaySubscribeInfo = - onboardingStatus === OnboardingStatus.CompletedWithoutSubscription; + onboardingStatus === OnboardingStatus.Completed && + !workspaceHasSubscription; const openBillingPortal = () => { if (isDefined(data) && isDefined(data.billingPortalSession.url)) { diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts index 7b5d380c46a..8370714f2b7 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/enums/onboarding-status.enum.ts @@ -1,10 +1,6 @@ export enum OnboardingStatus { - SUBSCRIPTION_CANCELED = 'SUBSCRIPTION_CANCELED', - SUBSCRIPTION_PAST_DUE = 'SUBSCRIPTION_PAST_DUE', - SUBSCRIPTION_UNPAID = 'SUBSCRIPTION_UNPAID', - WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', - COMPLETED_WITHOUT_SUBSCRIPTION = 'COMPLETED_WITHOUT_SUBSCRIPTION', PLAN_REQUIRED = 'PLAN_REQUIRED', + WORKSPACE_ACTIVATION = 'WORKSPACE_ACTIVATION', PROFILE_CREATION = 'PROFILE_CREATION', SYNC_EMAIL = 'SYNC_EMAIL', INVITE_TEAM = 'INVITE_TEAM', diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index affc5dd2575..a9e2203b903 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -100,39 +100,6 @@ export class OnboardingService { ); } - private async isSubscriptionCanceledOnboardingStatus(user: User) { - return ( - this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.Canceled - ); - } - - private async isSubscriptionPastDueOnboardingStatus(user: User) { - return ( - this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.PastDue - ); - } - - private async isSubscriptionUnpaidOnboardingStatus(user: User) { - return ( - this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.Unpaid - ); - } - - private async isCompletedWithoutSubscriptionOnboardingStatus(user: User) { - const currentBillingSubscription = - await this.billingService.getCurrentBillingSubscription({ - workspaceId: user.defaultWorkspaceId, - }); - - return ( - this.environmentService.get('IS_BILLING_ENABLED') && - !currentBillingSubscription - ); - } - async getOnboardingStatus(user: User) { if (await this.isSubscriptionIncompleteOnboardingStatus(user)) { return OnboardingStatus.PLAN_REQUIRED; @@ -154,22 +121,6 @@ export class OnboardingService { return OnboardingStatus.INVITE_TEAM; } - if (await this.isSubscriptionCanceledOnboardingStatus(user)) { - return OnboardingStatus.SUBSCRIPTION_CANCELED; - } - - if (await this.isSubscriptionPastDueOnboardingStatus(user)) { - return OnboardingStatus.SUBSCRIPTION_PAST_DUE; - } - - if (await this.isSubscriptionUnpaidOnboardingStatus(user)) { - return OnboardingStatus.SUBSCRIPTION_UNPAID; - } - - if (await this.isCompletedWithoutSubscriptionOnboardingStatus(user)) { - return OnboardingStatus.COMPLETED_WITHOUT_SUBSCRIPTION; - } - return OnboardingStatus.COMPLETED; } From aaf720ed1f0adca9aa214878ab5f65af465f32b3 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 19:22:38 +0200 Subject: [PATCH 34/47] Fix danger info button --- .../ui/display/info/components/Info.tsx | 1 + .../src/pages/settings/SettingsBilling.tsx | 31 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx b/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx index 822c193e8d1..44f61dd057a 100644 --- a/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx +++ b/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx @@ -65,6 +65,7 @@ export const Info = ({ onClick={onClick} size={'small'} variant={'secondary'} + accent={accent} /> )} diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index 916e055edcb..73b6725907b 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -23,7 +23,6 @@ import { Button } from '@/ui/input/button/components/Button'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; -import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; import { @@ -117,6 +116,10 @@ export const SettingsBilling = () => { } }; + const openPlanRequired = () => { + window.location.replace(AppPath.PlanRequired); + }; + const openSwitchingIntervalModal = () => { setIsSwitchingIntervalModalOpen(true); }; @@ -161,22 +164,20 @@ export const SettingsBilling = () => { /> )} {displaySubscriptionCanceledInfo && ( - - - + )} {displaySubscribeInfo ? ( - - - + ) : ( <>
From 1756d1194448ead4257e1b7464252118a990125c Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 19:23:49 +0200 Subject: [PATCH 35/47] Remove useless parameter --- .../onboarding/hooks/useSetNextOnboardingStatus.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index 086e0d713c7..a0875d03fd1 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -1,10 +1,6 @@ -import { useRecoilCallback, useRecoilValue } from 'recoil'; +import { useRecoilCallback } from 'recoil'; import { CurrentUser, currentUserState } from '@/auth/states/currentUserState'; -import { - CurrentWorkspace, - currentWorkspaceState, -} from '@/auth/states/currentWorkspaceState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; @@ -14,7 +10,6 @@ import { isDefined } from '~/utils/isDefined'; const getNextOnboardingStatus = ( currentUser: CurrentUser | null, workspaceMembers: WorkspaceMember[], - currentWorkspace: CurrentWorkspace | null, ) => { if (currentUser?.onboardingStatus === OnboardingStatus.ProfileCreation) { return OnboardingStatus.SyncEmail; @@ -32,7 +27,6 @@ export const useSetNextOnboardingStatus = () => { const { records: workspaceMembers } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, }); - const currentWorkspace = useRecoilValue(currentWorkspaceState); return useRecoilCallback( ({ snapshot, set }) => @@ -41,7 +35,6 @@ export const useSetNextOnboardingStatus = () => { const nextOnboardingStatus = getNextOnboardingStatus( currentUser, workspaceMembers, - currentWorkspace, ); set(currentUserState, (current) => { if (isDefined(current)) { @@ -53,6 +46,6 @@ export const useSetNextOnboardingStatus = () => { return current; }); }, - [workspaceMembers, currentWorkspace], + [workspaceMembers], ); }; From a061b65659ce34ff0c95f995f9dab0c59c6d2bed Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 26 Jun 2024 20:09:09 +0200 Subject: [PATCH 36/47] Fix useSetNextOnboardingStatus --- .../useSetNextOnboardingStatus.test.ts | 73 ++++++++++++------- .../hooks/useSetNextOnboardingStatus.ts | 10 +-- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts index 85b6b631af4..223370bce72 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/__tests__/useSetNextOnboardingStatus.test.ts @@ -1,5 +1,5 @@ -import { renderHook } from '@testing-library/react'; -import { RecoilRoot, useRecoilValue, useSetRecoilState } from 'recoil'; +import { act, renderHook } from '@testing-library/react'; +import { RecoilRoot, useRecoilState, useSetRecoilState } from 'recoil'; import { v4 } from 'uuid'; import { currentUserState } from '@/auth/states/currentUserState'; @@ -14,57 +14,74 @@ import { jest.mock('@/object-record/hooks/useFindManyRecords', () => ({ useFindManyRecords: jest.fn(), })); +const setupMockWorkspaceMembers = (withManyWorkspaceMembers = false) => { + jest + .requireMock('@/object-record/hooks/useFindManyRecords') + .useFindManyRecords.mockReturnValue({ + records: withManyWorkspaceMembers ? [{}, {}] : [{}], + }); +}; const renderHooks = ( onboardingStatus: OnboardingStatus, withCurrentBillingSubscription: boolean, - withManyWorkspaceMembers: boolean, ) => { const { result } = renderHook( () => { - const useFindManyRecordsMock = jest.requireMock( - '@/object-record/hooks/useFindManyRecords', - ); - useFindManyRecordsMock.useFindManyRecords.mockReturnValue({ - records: withManyWorkspaceMembers ? [{}, {}] : [{}], - }); - const setCurrentUser = useSetRecoilState(currentUserState); + const [currentUser, setCurrentUser] = useRecoilState(currentUserState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); - setCurrentUser({ ...mockedUserData, onboardingStatus }); - setCurrentWorkspace({ - ...mockDefaultWorkspace, - currentBillingSubscription: withCurrentBillingSubscription - ? { id: v4(), status: SubscriptionStatus.Active } - : undefined, - }); - useSetNextOnboardingStatus()(); - return useRecoilValue(currentUserState)?.onboardingStatus; + const setNextOnboardingStatus = useSetNextOnboardingStatus(); + return { + currentUser, + setCurrentUser, + setCurrentWorkspace, + setNextOnboardingStatus, + }; }, { wrapper: RecoilRoot, }, ); - return result; + act(() => { + result.current.setCurrentUser({ ...mockedUserData, onboardingStatus }); + result.current.setCurrentWorkspace({ + ...mockDefaultWorkspace, + currentBillingSubscription: withCurrentBillingSubscription + ? { id: v4(), status: SubscriptionStatus.Active } + : undefined, + }); + }); + act(() => { + result.current.setNextOnboardingStatus(); + }); + return result.current.currentUser?.onboardingStatus; }; describe('useSetNextOnboardingStatus', () => { it('should set next onboarding status for ProfileCreation', () => { - const result = renderHooks(OnboardingStatus.ProfileCreation, false, false); - expect(result.current).toEqual(OnboardingStatus.SyncEmail); + setupMockWorkspaceMembers(); + const nextOnboardingStatus = renderHooks( + OnboardingStatus.ProfileCreation, + false, + ); + expect(nextOnboardingStatus).toEqual(OnboardingStatus.SyncEmail); }); it('should set next onboarding status for SyncEmail', () => { - const result = renderHooks(OnboardingStatus.SyncEmail, false, false); - expect(result.current).toEqual(OnboardingStatus.InviteTeam); + setupMockWorkspaceMembers(); + const nextOnboardingStatus = renderHooks(OnboardingStatus.SyncEmail, false); + expect(nextOnboardingStatus).toEqual(OnboardingStatus.InviteTeam); }); it('should skip invite when workspaceMembers exist', () => { - const result = renderHooks(OnboardingStatus.SyncEmail, true, true); - expect(result.current).toEqual(OnboardingStatus.Completed); + setupMockWorkspaceMembers(true); + const nextOnboardingStatus = renderHooks(OnboardingStatus.SyncEmail, true); + expect(nextOnboardingStatus).toEqual(OnboardingStatus.Completed); }); it('should set next onboarding status for Completed', () => { - const result = renderHooks(OnboardingStatus.InviteTeam, true, false); - expect(result.current).toEqual(OnboardingStatus.Completed); + setupMockWorkspaceMembers(); + const nextOnboardingStatus = renderHooks(OnboardingStatus.InviteTeam, true); + expect(nextOnboardingStatus).toEqual(OnboardingStatus.Completed); }); }); diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts index a0875d03fd1..4e7353e8b68 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useSetNextOnboardingStatus.ts @@ -1,4 +1,4 @@ -import { useRecoilCallback } from 'recoil'; +import { useRecoilCallback, useRecoilValue } from 'recoil'; import { CurrentUser, currentUserState } from '@/auth/states/currentUserState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; @@ -27,11 +27,11 @@ export const useSetNextOnboardingStatus = () => { const { records: workspaceMembers } = useFindManyRecords({ objectNameSingular: CoreObjectNameSingular.WorkspaceMember, }); + const currentUser = useRecoilValue(currentUserState); return useRecoilCallback( - ({ snapshot, set }) => + ({ set }) => () => { - const currentUser = snapshot.getLoadable(currentUserState).getValue(); const nextOnboardingStatus = getNextOnboardingStatus( currentUser, workspaceMembers, @@ -40,12 +40,12 @@ export const useSetNextOnboardingStatus = () => { if (isDefined(current)) { return { ...current, - onboardingStatus: nextOnboardingStatus as OnboardingStatus, + onboardingStatus: nextOnboardingStatus, }; } return current; }); }, - [workspaceMembers], + [workspaceMembers, currentUser], ); }; From a4350c8f7067016f5c1e8103694ae3bf4c63c3cb Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 27 Jun 2024 15:57:47 +0200 Subject: [PATCH 37/47] Remove core.workspace.subscriptionStatus column --- .../src/generated/graphql.tsx | 230 +++++++++++------- .../src/generated-metadata/graphql.ts | 7 +- .../twenty-front/src/generated/graphql.tsx | 13 +- .../auth/states/currentWorkspaceState.ts | 1 - .../graphql/fragments/userQueryFragment.ts | 1 - .../graphql/mutations/updateWorkspace.ts | 1 - .../__tests__/useSubscriptionStatus.test.ts | 13 +- .../workspace/hooks/useSubscriptionStatus.ts | 5 +- .../src/testing/mock-data/users.ts | 1 - .../database/typeorm-seeds/core/workspaces.ts | 10 +- ...moveSubscriptionStatusFromCoreWorkspace.ts | 25 ++ .../auth/services/sign-in-up.service.ts | 4 +- .../core-modules/billing/billing.service.ts | 4 - .../feature-flag/feature-flag.entity.ts | 1 + .../onboarding/onboarding.module.ts | 3 + .../onboarding/onboarding.service.ts | 34 ++- .../workspace/workspace.entity.ts | 15 +- .../delete-incomplete-workspaces.command.ts | 2 +- .../commands/add-standard-id.command.ts | 2 + .../jobs/google-calendar-sync.cron.job.ts | 12 +- .../messaging-message-list-fetch.cron.job.ts | 12 +- .../messaging-messages-import.cron.job.ts | 12 +- .../jobs/messaging-ongoing-stale.cron.job.ts | 12 +- ...age-channel-sync-status-monitoring.cron.ts | 12 +- 24 files changed, 272 insertions(+), 160 deletions(-) create mode 100644 packages/twenty-server/src/database/typeorm/core/migrations/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts diff --git a/packages/twenty-chrome-extension/src/generated/graphql.tsx b/packages/twenty-chrome-extension/src/generated/graphql.tsx index d48802b3177..2ced7aaa8fd 100644 --- a/packages/twenty-chrome-extension/src/generated/graphql.tsx +++ b/packages/twenty-chrome-extension/src/generated/graphql.tsx @@ -397,6 +397,10 @@ export type Analytics = { success: Scalars['Boolean']; }; +export type ApiConfig = { + mutationMaximumAffectedRecords: Scalars['Float']; +}; + /** An api key */ export type ApiKey = { /** Creation date */ @@ -1719,6 +1723,7 @@ export enum CaptchaDriverType { } export type ClientConfig = { + api: ApiConfig; authProviders: AuthProviders; billing: Billing; captcha: Captcha; @@ -3732,281 +3737,337 @@ export type MutationCheckoutSessionArgs = { export type MutationCreateActivitiesArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateActivityArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateActivityTargetArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateActivityTargetsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateApiKeyArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateApiKeysArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateAttachmentArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateAttachmentsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateAuditLogArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateAuditLogsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateBlocklistArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateBlocklistsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateCalendarChannelArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateCalendarChannelEventAssociationArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateCalendarChannelEventAssociationsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateCalendarChannelsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateCalendarEventArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateCalendarEventParticipantArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateCalendarEventParticipantsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateCalendarEventsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateCommentArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateCommentsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateCompaniesArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateCompanyArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateConnectedAccountArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateConnectedAccountsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateFavoriteArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateFavoritesArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateMessageArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateMessageChannelArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateMessageChannelMessageAssociationArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateMessageChannelMessageAssociationsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateMessageChannelsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateMessageParticipantArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateMessageParticipantsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateMessageThreadArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateMessageThreadsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateMessagesArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateOpportunitiesArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateOpportunityArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreatePeopleArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreatePersonArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateTimelineActivitiesArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateTimelineActivityArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateViewArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateViewFieldArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateViewFieldsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateViewFilterArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateViewFiltersArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateViewSortArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateViewSortsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateViewsArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateWebhookArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateWebhooksArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; export type MutationCreateWorkspaceMemberArgs = { data?: InputMaybe; + upsert?: InputMaybe; }; export type MutationCreateWorkspaceMembersArgs = { data?: InputMaybe>; + upsert?: InputMaybe; }; @@ -5434,51 +5495,51 @@ export type ProductPricesEntity = { export type Query = { activities?: Maybe; activity?: Maybe; - activityDuplicates?: Maybe; + activityDuplicates?: Maybe>; activityTarget?: Maybe; - activityTargetDuplicates?: Maybe; + activityTargetDuplicates?: Maybe>; activityTargets?: Maybe; apiKey?: Maybe; - apiKeyDuplicates?: Maybe; + apiKeyDuplicates?: Maybe>; apiKeys?: Maybe; attachment?: Maybe; - attachmentDuplicates?: Maybe; + attachmentDuplicates?: Maybe>; attachments?: Maybe; auditLog?: Maybe; - auditLogDuplicates?: Maybe; + auditLogDuplicates?: Maybe>; auditLogs?: Maybe; billingPortalSession: SessionEntity; blocklist?: Maybe; - blocklistDuplicates?: Maybe; + blocklistDuplicates?: Maybe>; blocklists?: Maybe; calendarChannel?: Maybe; - calendarChannelDuplicates?: Maybe; + calendarChannelDuplicates?: Maybe>; calendarChannelEventAssociation?: Maybe; - calendarChannelEventAssociationDuplicates?: Maybe; + calendarChannelEventAssociationDuplicates?: Maybe>; calendarChannelEventAssociations?: Maybe; calendarChannels?: Maybe; calendarEvent?: Maybe; - calendarEventDuplicates?: Maybe; + calendarEventDuplicates?: Maybe>; calendarEventParticipant?: Maybe; - calendarEventParticipantDuplicates?: Maybe; + calendarEventParticipantDuplicates?: Maybe>; calendarEventParticipants?: Maybe; calendarEvents?: Maybe; checkUserExists: UserExists; checkWorkspaceInviteHashIsValid: WorkspaceInviteHashValid; clientConfig: ClientConfig; comment?: Maybe; - commentDuplicates?: Maybe; + commentDuplicates?: Maybe>; comments?: Maybe; companies?: Maybe; company?: Maybe; - companyDuplicates?: Maybe; + companyDuplicates?: Maybe>; connectedAccount?: Maybe; - connectedAccountDuplicates?: Maybe; + connectedAccountDuplicates?: Maybe>; connectedAccounts?: Maybe; currentUser: User; currentWorkspace: Workspace; favorite?: Maybe; - favoriteDuplicates?: Maybe; + favoriteDuplicates?: Maybe>; favorites?: Maybe; findWorkspaceFromInviteHash: Workspace; getPostgresCredentials?: Maybe; @@ -5489,48 +5550,48 @@ export type Query = { getTimelineThreadsFromPersonId: TimelineThreadsWithTotal; message?: Maybe; messageChannel?: Maybe; - messageChannelDuplicates?: Maybe; + messageChannelDuplicates?: Maybe>; messageChannelMessageAssociation?: Maybe; - messageChannelMessageAssociationDuplicates?: Maybe; + messageChannelMessageAssociationDuplicates?: Maybe>; messageChannelMessageAssociations?: Maybe; messageChannels?: Maybe; - messageDuplicates?: Maybe; + messageDuplicates?: Maybe>; messageParticipant?: Maybe; - messageParticipantDuplicates?: Maybe; + messageParticipantDuplicates?: Maybe>; messageParticipants?: Maybe; messageThread?: Maybe; - messageThreadDuplicates?: Maybe; + messageThreadDuplicates?: Maybe>; messageThreads?: Maybe; messages?: Maybe; object: Object; objects: ObjectConnection; opportunities?: Maybe; opportunity?: Maybe; - opportunityDuplicates?: Maybe; + opportunityDuplicates?: Maybe>; people?: Maybe; person?: Maybe; - personDuplicates?: Maybe; + personDuplicates?: Maybe>; timelineActivities?: Maybe; timelineActivity?: Maybe; - timelineActivityDuplicates?: Maybe; + timelineActivityDuplicates?: Maybe>; validatePasswordResetToken: ValidatePasswordResetToken; view?: Maybe; - viewDuplicates?: Maybe; + viewDuplicates?: Maybe>; viewField?: Maybe; - viewFieldDuplicates?: Maybe; + viewFieldDuplicates?: Maybe>; viewFields?: Maybe; viewFilter?: Maybe; - viewFilterDuplicates?: Maybe; + viewFilterDuplicates?: Maybe>; viewFilters?: Maybe; viewSort?: Maybe; - viewSortDuplicates?: Maybe; + viewSortDuplicates?: Maybe>; viewSorts?: Maybe; views?: Maybe; webhook?: Maybe; - webhookDuplicates?: Maybe; + webhookDuplicates?: Maybe>; webhooks?: Maybe; workspaceMember?: Maybe; - workspaceMemberDuplicates?: Maybe; + workspaceMemberDuplicates?: Maybe>; workspaceMembers?: Maybe; }; @@ -5552,8 +5613,8 @@ export type QueryActivityArgs = { export type QueryActivityDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5563,8 +5624,8 @@ export type QueryActivityTargetArgs = { export type QueryActivityTargetDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5585,8 +5646,8 @@ export type QueryApiKeyArgs = { export type QueryApiKeyDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5607,8 +5668,8 @@ export type QueryAttachmentArgs = { export type QueryAttachmentDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5629,8 +5690,8 @@ export type QueryAuditLogArgs = { export type QueryAuditLogDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5656,8 +5717,8 @@ export type QueryBlocklistArgs = { export type QueryBlocklistDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5678,8 +5739,8 @@ export type QueryCalendarChannelArgs = { export type QueryCalendarChannelDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5689,8 +5750,8 @@ export type QueryCalendarChannelEventAssociationArgs = { export type QueryCalendarChannelEventAssociationDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5722,8 +5783,8 @@ export type QueryCalendarEventArgs = { export type QueryCalendarEventDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5733,8 +5794,8 @@ export type QueryCalendarEventParticipantArgs = { export type QueryCalendarEventParticipantDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5777,8 +5838,8 @@ export type QueryCommentArgs = { export type QueryCommentDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5810,8 +5871,8 @@ export type QueryCompanyArgs = { export type QueryCompanyDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5821,8 +5882,8 @@ export type QueryConnectedAccountArgs = { export type QueryConnectedAccountDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5843,8 +5904,8 @@ export type QueryFavoriteArgs = { export type QueryFavoriteDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5908,8 +5969,8 @@ export type QueryMessageChannelArgs = { export type QueryMessageChannelDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5919,8 +5980,8 @@ export type QueryMessageChannelMessageAssociationArgs = { export type QueryMessageChannelMessageAssociationDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5947,8 +6008,8 @@ export type QueryMessageChannelsArgs = { export type QueryMessageDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5958,8 +6019,8 @@ export type QueryMessageParticipantArgs = { export type QueryMessageParticipantDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -5980,8 +6041,8 @@ export type QueryMessageThreadArgs = { export type QueryMessageThreadDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6024,8 +6085,8 @@ export type QueryOpportunityArgs = { export type QueryOpportunityDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6046,8 +6107,8 @@ export type QueryPersonArgs = { export type QueryPersonDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6068,8 +6129,8 @@ export type QueryTimelineActivityArgs = { export type QueryTimelineActivityDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6084,8 +6145,8 @@ export type QueryViewArgs = { export type QueryViewDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6095,8 +6156,8 @@ export type QueryViewFieldArgs = { export type QueryViewFieldDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6117,8 +6178,8 @@ export type QueryViewFilterArgs = { export type QueryViewFilterDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6139,8 +6200,8 @@ export type QueryViewSortArgs = { export type QueryViewSortDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6172,8 +6233,8 @@ export type QueryWebhookArgs = { export type QueryWebhookDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -6194,8 +6255,8 @@ export type QueryWorkspaceMemberArgs = { export type QueryWorkspaceMemberDuplicatesArgs = { - data?: InputMaybe; - id?: InputMaybe; + data?: InputMaybe>>; + ids?: InputMaybe>>; }; @@ -7327,7 +7388,6 @@ export type Workspace = { id: Scalars['UUID']; inviteHash?: Maybe; logo?: Maybe; - subscriptionStatus: SubscriptionStatus; updatedAt: Scalars['DateTime']; }; diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 778702cb208..8098afa1420 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -36,6 +36,11 @@ export type Analytics = { success: Scalars['Boolean']['output']; }; +export type ApiConfig = { + __typename?: 'ApiConfig'; + mutationMaximumAffectedRecords: Scalars['Float']['output']; +}; + export type ApiKeyToken = { __typename?: 'ApiKeyToken'; token: Scalars['String']['output']; @@ -142,6 +147,7 @@ export enum CaptchaDriverType { export type ClientConfig = { __typename?: 'ClientConfig'; + api: ApiConfig; authProviders: AuthProviders; billing: Billing; captcha: Captcha; @@ -1196,7 +1202,6 @@ export type Workspace = { id: Scalars['UUID']['output']; inviteHash?: Maybe; logo?: Maybe; - subscriptionStatus: SubscriptionStatus; updatedAt: Scalars['DateTime']['output']; }; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 2967199c1bc..124daad290d 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -918,7 +918,6 @@ export type Workspace = { id: Scalars['UUID']; inviteHash?: Maybe; logo?: Maybe; - subscriptionStatus: SubscriptionStatus; updatedAt: Scalars['DateTime']; }; @@ -1178,7 +1177,7 @@ export type ImpersonateMutationVariables = Exact<{ }>; -export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type ImpersonateMutation = { __typename?: 'Mutation', impersonate: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type RenewTokenMutationVariables = Exact<{ appToken: Scalars['String']; @@ -1210,7 +1209,7 @@ export type VerifyMutationVariables = Exact<{ }>; -export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; +export type VerifyMutation = { __typename?: 'Mutation', verify: { __typename?: 'Verify', user: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }, tokens: { __typename?: 'AuthTokenPair', accessToken: { __typename?: 'AuthToken', token: string, expiresAt: string }, refreshToken: { __typename?: 'AuthToken', token: string, expiresAt: string } } } }; export type CheckUserExistsQueryVariables = Exact<{ email: Scalars['String']; @@ -1264,7 +1263,7 @@ export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string] export type SkipSyncEmailOnboardingStepMutation = { __typename?: 'Mutation', skipSyncEmailOnboardingStep: { __typename?: 'OnboardingStepSuccess', success: boolean } }; -export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }; +export type UserQueryFragmentFragment = { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> }; export type DeleteUserAccountMutationVariables = Exact<{ [key: string]: never; }>; @@ -1281,7 +1280,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, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } }; +export type GetCurrentUserQuery = { __typename?: 'Query', currentUser: { __typename?: 'User', id: any, firstName: string, lastName: string, email: string, canImpersonate: boolean, supportUserHash?: string | null, onboardingStatus?: OnboardingStatus | null, workspaceMember?: { __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale: string, name: { __typename?: 'FullName', firstName: string, lastName: string } } | null, defaultWorkspace: { __typename?: 'Workspace', id: any, displayName?: string | null, logo?: string | null, domainName?: string | null, inviteHash?: string | null, allowImpersonation: boolean, activationStatus: string, currentCacheVersion?: string | null, featureFlags?: Array<{ __typename?: 'FeatureFlag', id: any, key: string, value: boolean, workspaceId: string }> | null, currentBillingSubscription?: { __typename?: 'BillingSubscription', id: any, status: SubscriptionStatus, interval?: SubscriptionInterval | null } | null }, workspaces: Array<{ __typename?: 'UserWorkspace', workspace?: { __typename?: 'Workspace', id: any, logo?: string | null, displayName?: string | null, domainName?: string | null } | null }> } }; export type AddUserToWorkspaceMutationVariables = Exact<{ inviteHash: Scalars['String']; @@ -1314,7 +1313,7 @@ export type UpdateWorkspaceMutationVariables = Exact<{ }>; -export type UpdateWorkspaceMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: any, domainName?: string | null, displayName?: string | null, logo?: string | null, allowImpersonation: boolean, subscriptionStatus: SubscriptionStatus } }; +export type UpdateWorkspaceMutation = { __typename?: 'Mutation', updateWorkspace: { __typename?: 'Workspace', id: any, domainName?: string | null, displayName?: string | null, logo?: string | null, allowImpersonation: boolean } }; export type UploadWorkspaceLogoMutationVariables = Exact<{ file: Scalars['Upload']; @@ -1443,7 +1442,6 @@ export const UserQueryFragmentFragmentDoc = gql` domainName inviteHash allowImpersonation - subscriptionStatus activationStatus featureFlags { id @@ -2685,7 +2683,6 @@ export const UpdateWorkspaceDocument = gql` displayName logo allowImpersonation - subscriptionStatus } } `; diff --git a/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts b/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts index c9187c3ea69..239dfc79304 100644 --- a/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts +++ b/packages/twenty-front/src/modules/auth/states/currentWorkspaceState.ts @@ -10,7 +10,6 @@ export type CurrentWorkspace = Pick< | 'displayName' | 'allowImpersonation' | 'featureFlags' - | 'subscriptionStatus' | 'activationStatus' | 'currentBillingSubscription' | 'currentCacheVersion' diff --git a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts index d3a65b504d8..7b46e794f71 100644 --- a/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts +++ b/packages/twenty-front/src/modules/users/graphql/fragments/userQueryFragment.ts @@ -26,7 +26,6 @@ export const USER_QUERY_FRAGMENT = gql` domainName inviteHash allowImpersonation - subscriptionStatus activationStatus featureFlags { id diff --git a/packages/twenty-front/src/modules/workspace/graphql/mutations/updateWorkspace.ts b/packages/twenty-front/src/modules/workspace/graphql/mutations/updateWorkspace.ts index c244ce04109..1d9a9b9fbed 100644 --- a/packages/twenty-front/src/modules/workspace/graphql/mutations/updateWorkspace.ts +++ b/packages/twenty-front/src/modules/workspace/graphql/mutations/updateWorkspace.ts @@ -8,7 +8,6 @@ export const UPDATE_WORKSPACE = gql` displayName logo allowImpersonation - subscriptionStatus } } `; diff --git a/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts b/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts index d6cb6d3116e..eaaa6a8218c 100644 --- a/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts +++ b/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts @@ -1,6 +1,7 @@ import { act } from 'react-dom/test-utils'; import { renderHook } from '@testing-library/react'; import { RecoilRoot, useSetRecoilState } from 'recoil'; +import { v4 } from 'uuid'; import { CurrentWorkspace, @@ -17,7 +18,7 @@ const tokenPair = { }; const currentWorkspace = { id: '1', - subscriptionStatus: SubscriptionStatus.Incomplete, + currentBillingSubscription: { status: SubscriptionStatus.Incomplete }, activationStatus: 'active', allowImpersonation: true, } as CurrentWorkspace; @@ -59,7 +60,10 @@ describe('useSubscriptionStatus', () => { setTokenPair(tokenPair); setCurrentWorkspace({ ...currentWorkspace, - subscriptionStatus, + currentBillingSubscription: { + id: v4(), + status: subscriptionStatus, + }, }); }); expect(result.current.subscriptionStatus).toBe(SubscriptionStatus.Active); @@ -76,7 +80,10 @@ describe('useSubscriptionStatus', () => { setTokenPair(tokenPair); setCurrentWorkspace({ ...currentWorkspace, - subscriptionStatus, + currentBillingSubscription: { + id: v4(), + status: subscriptionStatus, + }, }); }); diff --git a/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts b/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts index 7c11f5990d0..d0b8d4a9aab 100644 --- a/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts +++ b/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts @@ -18,5 +18,8 @@ export const useSubscriptionStatus = (): if (!billing?.isBillingEnabled) { return SubscriptionStatus.Active; } - return currentWorkspace?.subscriptionStatus; + return ( + currentWorkspace?.currentBillingSubscription?.status || + SubscriptionStatus.Incomplete + ); }; diff --git a/packages/twenty-front/src/testing/mock-data/users.ts b/packages/twenty-front/src/testing/mock-data/users.ts index a50ead14a19..1afd532d260 100644 --- a/packages/twenty-front/src/testing/mock-data/users.ts +++ b/packages/twenty-front/src/testing/mock-data/users.ts @@ -36,7 +36,6 @@ export const mockDefaultWorkspace: Workspace = { inviteHash: 'twenty.com-invite-hash', logo: workspaceLogoUrl, allowImpersonation: true, - subscriptionStatus: SubscriptionStatus.Active, activationStatus: 'active', featureFlags: [ { diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/workspaces.ts b/packages/twenty-server/src/database/typeorm-seeds/core/workspaces.ts index 1ebe78147c1..71fcea2d4bb 100644 --- a/packages/twenty-server/src/database/typeorm-seeds/core/workspaces.ts +++ b/packages/twenty-server/src/database/typeorm-seeds/core/workspaces.ts @@ -15,12 +15,7 @@ export const seedWorkspaces = async ( const workspaces: { [key: string]: Pick< Workspace, - | 'id' - | 'displayName' - | 'domainName' - | 'inviteHash' - | 'logo' - | 'subscriptionStatus' + 'id' | 'displayName' | 'domainName' | 'inviteHash' | 'logo' >; } = { [SEED_APPLE_WORKSPACE_ID]: { @@ -29,7 +24,6 @@ export const seedWorkspaces = async ( domainName: 'apple.dev', inviteHash: 'apple.dev-invite-hash', logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAAAXNSR0IArs4c6QAAELpJREFUeF7tnXnwr3MVx1+WS5Hthoss1R0lNSTUKGQdt6EscVVCKVPKUqmUKcu0WJI2ppJUt0kNwmRN0dBCtolKpqIVIdl3qXnX8xs/1+937/d5vp/lnOc5n3++/zyfzznnfc77+zyf7ZxFiBYIBALTIrBIYBMIBALTIxAEiegIBBaAQBAkwiMQCIJEDIyJgP5IlwRmAreOOZar7vEGceWuYsrOBg4E5gKzgMlxMqiYGZSxxcLLnyDFwU7AF4A1FqL+osB//JnYTeMgSDfc+tLrhcBZwHotDFoMeLLF864fDYK4dl9n5XcBTm3mFW0HWRz4d9tOXp8Pgnj1XDe9d2+Ioc+kri3eIF2Ri35mEdgAuLzjG2N+o2IOYtbNoVhbBGYA1wPrtO24gOcH9dUxKGMTBomHofYE5iVWVKtX43yeJVYn/3BBkPwY15BwXcuVqVF1fBB4zqgP9+G5IEgfvPiUDSsAdwBaacrRrgY2zjGw1TGDIFY9016vjYCr2ndr1eMQ4NhWPZw/HARx7sBG/Z2BMwuY8nzgLwXkmBERBDHjis6KaG/je517t+s4uHgZnMHt4sH809sCFxXS8l5g+UKyzIgJgphxRWtFdI7qpta9unc4Ejiie3efPYMgPv2muxmPFFZdy7ta5h1UC4L4dPcDwNIFVX8YWKqgPDOigiBmXDGyIhcAc0Z+Os2D+wFfSTOUr1GCIL78tTXw4woqD+qA4mR8gyAVoq2jSAVpjXsY2l95Y0ed3XcLgvhx4S+BV1ZQdwng8QpyTYgMgphww0KVeDFw40KfSv/AD4Ad0w/rZ8QgiA9fPQQ8u4Kqg7o9OBW+QZAKUddSpLKNKLFC6XYocFRpodbkBUGseeSZ+uj7P9fx9ems1yZkjTeWOW8EQcy55GkKab9D+x6l2+rALaWFWpQXBLHolad0Kr1jLsknAAfYhqWcdkGQcli3lbQKcFvbTmM+fxew4phj9Kp7EMSuOy8Btiys3qD3PKbCOghSOAJbiCud//alwA0t9BvEo0EQm25+GfDrgqrtC5xcUJ4bUUEQm666ENiukGrK6P6+QrLciQmC2HTZE4B2sXO305saILnluB0/CGLPdSKGCJK7nQu8PrcQ7+MHQex5cENACdpytu8Cb8kpoC9jB0HsefI44OCMaul8lc5ZRRsBgSDICCAVfuQ3gJZcczQVzqlx8DGHLUXGDIIUgbmVEGUOSZ0gQSXTdL6q9M58K8MtPhwEsecVBXNKvyjTuwrolN54tIdsB41SOqKD+OgyBQIpA/kNwDmBcncEgiDdscvVMwVBzmuWcFOMlctOF+MGQey5aZyg/hWwGaBj8tESIBAESQBi4iG6EOQ0YO8K6UgTm25vuCCIPZ+MMkkXiZQGSBebcm8q2kOooEZBkIJgjyhKV11nNitZIoI+l/4O/Aw4A7gCeHTEscZ5TLExcRdeeihpXZe32zg6VO87ZILoctDzgPWbZdC1gdWAZRqv3An8Dris+bfWHkLfAkQEWA94E7A9oJIKz1pIVAoDJXX4M/DTZuNRNdhVP6R3bSgEUSAor+37m0ls1404ff4ogdvnmqpO3ibDKpugNKIfbXbrU/tfxPkRcHTzphNerltqgCyBMQs4vjnOnSttjv5N9S8q4l1ryfhJumgHXee7di10hH4yDDoVINnHACqh4K71jSAigv69dAGoxH2K+R3+W+Cdzb9nzWBYFfgqsEPiXflxbBJZDgJO8fSp2heCKCBUFmDdcTyYuO9PgL2aCXbioaccTnOHTzVBWOPPoY2Nyvk7t9BiQxu9nvGsd4JoUq3a4Pq12rT6oznLYRk+M0QEvbH0GaMSad6aVua2sUwUrwTRZPPKZgXGU1BoUi+iKDlb15ICqhOiVSeRbmVPxi9A1283b1tz5ngkyKebVRhzYLZUSGTRP/9nRzgaoiXpPZv5VV8Tu2nBQyuN+jQ10zwRRP+WWnvvY1JlBYdWwU5s5lLaU9ClKS02KLu7CDKUphMCm1iZyHshyOFDrNE9FEZMYafmbWtZSKBtnSDS7w/A7AEHy5BNf3OzIVsNA8sEWRZQMuVcm3zVQA/BrRComtjOKkFq1eRr5bl4uBgC1SrtWiTIps3xjWLohyAXCFQpKGqNIFtYW+ZzETrDUfIbwD4lzbVEkI2aXfGS9ocsfwh8oNkkLaK5FYLoXoYuBUULBEZBQH+m14zy4LjPWCCINsF0j8CCLuPiGf3LIaDDmdlvVloIyvudHrQrFwohaSoE7m6uJmdFpzZBdERd52+iBQJtECh2bqsmQXYEzm6DSjwbCDQ5ArTaWSQ/QC2CzAAeC3cHAi0R0N2Ri1v2GevxWgS5qcmgMZby0XkwCNwDKMdA8T/VGgSZA1wwGNeGoeMioDeG3hxVWg2CjJI5sAoYIdQcAp8EPl5Tq9IE+Vpzh7qmzSHbBwLKNTyvtqolCaINwewbO7UBDflJEFByO53grd5KEuRSYPPqFocC1hHYAzjVipKlCKJjAS4z61lx1ED00HxD8w4zrRRBLgS2M2N1KGIRgfObBNqmdCtBEOVx0iX8aIHAdAgUOVfVBf4SBImMJF08M6w+SgRYfBNwFIhLEOSJSomkR7E/nqmPwM6Wz+TlJojS9fyxvg9CA6MI3Gw9pVNugig58WuMOifUqo+AsmTqspzZlpsgRY4km0U3FFsQAqoT8g7rEOUkyDpNjT/rGIR+dRDQ6qb5P9CcBDnX4rp2nVgIqfMhoOpX7/aASk6CxOqVhwioo6PSybrYG8tFkDiYWCfwPEi9vim97UHXbKl2VDzyHBcIhJKlEVDe5d+XFtpVXq43iG6BbdVVqejXWwQ0Kdfk3E3LRRCtbev4QLRAYDICKq/m6o8zF0HML99F3FZBYGPg6iqSOwrNQZBlgPs66hPd+o2Ai72PyS7IQZAtgUv67eewrgMCOq3r7rM7B0FU2vjgDgBGl34j8HNAxZFctRwE0Tfmhq5QCGVLILB/U+a6hKxkMnIQRFnwlkumYQzUFwRe1FQsdmVPDoLoCIGrtW5XHvOrbJF6HqnhyUGQWOJN7aV+jJcj1rIjk0PpIEh2t7kUkCPWsgORWunFAJ3ijRYITEbA3RGTCeVTE0Tr3KavUEbcVkFACcv15+mupSZI7KK7C4EiCuurQkWT3LXUBFkJuMMdCqFwbgSCIA3CqwK35kY7xneHQHxiNS5bDbjFnftC4dwIxCS9QXgV4LbcaMf4LhFI/TlfBITUSsccpIjbXApxd9RdKKcmyLLAvS7dF0rnRsB8FsWpAEhNkCiUkzvM/I7vKlnDBMypCRI76X4DOLfm7wG+nFtI6vFTE0TjaUkvWiAwPwKXAa/1Bktqgsj+OKzoLQrK6KsKx/oEd9VyEERvkBzjugI2lJ0SAXcrWTkCOXJiBTumQ2Bdbxn/cxBEO+naUY8WCMyPwBnAbp5gyUGQSDvqKQLK6qrr2Mrs7qblIMhhwJFuEAhFSyOg0xb/LC20q7wcBNkcuLSrQtGv9wicDaiyrYuWgyDLAyoMHy0QmA6BHHGXBe0cisZmYRZX9WrQvYF5HizKQRDZHXshHrxfT0c3NwxzEUR3QnQ3JFogMB0CmodoPmK65SLIyR5qYJv2TP+Vc3ENNxdBYiWr/wGewsKjgENTDJRrjFwEifxYuTzWv3GXAh62alYugsjeONVr1eu29Lrd8nw1J0H+BqxuyxehjVEEjrB6+iInQQ4HZHi0QGAUBEzWD8lJkEgiN0pYxDMTCOiTXHPXxy1BkpMgMQ+x5Gkfumiyrkm7mZabIP8AZpmxNhTxgIA2mc3cJ8pNkA8Cn/HgldDRFAI3A7MtaJSbIJFIzoKXfeqgVdA1a6uemyCyL4p61vayX/m6NvHcmntqJQhyEbCtXx+F5pUR0Mlf3TF6sIYeJQjyEuCGGsaFzF4h8Grg8tIWlSBILPeW9mp/5emU+L4lzStFkGuBDUoaFrJ6i8ADzeS9yLXuUgTZCLiqty4Lw2ogcBLwrtyCSxEkPrNye3KY4+t4ylxACemytJIEUXbvzbJYEYMOHQGd3xJRkl/hLUmQFwDaIY0WCORCQG8UbU5rnpKklSSIFI5NwyRui0GmQSB5tpTSBDkOODjcGwhkQuD41PFVmiBLACqkEi0QyIFA8kKhpQkiUP4KrJEDnRhz0AhoX2RmagRqEOQVwDWpDYnxBo/ALsBZqVGoQRDZoMmUKuJGCwRSIZAllrMMOoLFHwaOGeG5eCQQGAWBC4HXjfJg22dqESQywLf1VDy/IARWAO7JAVEtgsiW07zVq8vhgBhzbAT+1VyqGnugqQaoSZBY8s3i0sENmvWeSE2CyJO/ADYZnEvD4FQIJN85n1+x2gRZOuW5mVSoxzhuEHgr8J2c2tYmiGy7Gtgwp5Exdi8R0MHERXNbZoEg8RbJ7eV+jr8/cGJu0ywQRDaqbLSK7kQLBEZBoMjbQ4pYIcgM4LFRkIlnAoGmvN8pJZCwQhDZKoPfXsLokOEagewrV5PRsUQQ6RVVqVzHbhHltwYuKSLJ0CfWhL17Ad8qZXzIcYdA1l3zqdCw9gaRjvcBy7hzXShcAgGVRVB5hGLNIkGiMlUx97sSpM8qfV4VbRYJIgB08WWnokiEMOsI6P7Qk6WVtEoQ4RAZUEpHg115ewCn1lDPMkEiXWmNiLAns2pJNssEkasuBray57PQqCACKuqp4p5VmnWCCJS4v14lNEwIPQA4oaYmHggSKUtrRkg92UoPtVY98f+X7IEg0lOVclUxN9pwEND5PH09VG1eCCKQ7gBWqopWCC+FwBbNCe9S8qaV44kgceK3ergUUeB8YPsikkYQ4okgMkf313WPPVo/EdBqlVatzDRvBBFw3wT2NoNgKJISgeWas3gpxxxrLI8EkcG3AyuPZXl0tobA7k2uNFN6eSWIzuWo7JZX/U0FgQFlTM07JuPhOcBif8RAZCdQQSlDlTrUZPNMEAGquYjmJNF8IqAbpEs2XwMmLfBOEIGqG4i6iRjNHwKzrRd27QNBFBbXAev5i49Ba7wr8H3rCPSFIML5TmBF64CHfv9D4CjgUA9Y9IkgskX1sU1tNHkIgsI6ng7MLSyzs7g+EUQgKFfrQ83ErzMo0TEbAlXulY9jTd8IMkGS++NNMk5YZOn7Q2BOlpEzDtpHgggu2XUrsEpG7HIMraQE+kzUfEqnlx9shCwLzGpOM6sWuDe/nQzsmwOw3GN6A7otHio3rbLTFpvyf50JnARcCzzaUkltru0AvBfYuEQpgJb6TTx+EPDFjn2rd+s7QQTw5wE5qXbT2+Ec4CPAjRmU0YabNk4/Yeic2vrA9RlsLTbkEAgiMF8FXFEM1acLOq/5vCiZEVCLFfs0fw6qv1K6/QlYu0ndVFp2UnlDIYhAU9Dok+vlSRGcerC7AJUHU/3u2k2fYvOaz7HcuujoyG4eNgBHBWJIBJnARK/9ywFNdlM23Z/+UrMB9kjKgROOtQ3wdWDNhGNODHUscEiGcasOOUSCTAC+aTMnWH4MD2hirYD7GHD3GOOU7iq/K8/tcc0Rna5xoIWG/WplPSwBWldgSuhWSoZ23j8EHAjMXIhQXQm9DDi+qVFRPetGIpA0T9EexdsA1R3XZ9lUsaFN2Csb+y+wkHUkkf3TDhMEeSY0izcBoqDRypM2HfVPqVzBQ2yKkcEWNgqCDDHkw+aREQiCjAxVPDhEBIIgQ/R62DwyAkGQkaGKB4eIwH8BiW3y2J/F45oAAAAASUVORK5CYII=', - subscriptionStatus: 'incomplete', }, [SEED_TWENTY_WORKSPACE_ID]: { id: workspaceId, @@ -37,7 +31,6 @@ export const seedWorkspaces = async ( domainName: 'twenty.dev', inviteHash: 'twenty.dev-invite-hash', logo: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACb0lEQVR4nO2VO4taQRTHr3AblbjxEVlwCwVhg7BoqqCIjy/gAyyFWNlYBOxsfH0KuxgQGwXRUkGuL2S7i1barGAgiwbdW93SnGOc4BonPiKahf3DwXFmuP/fPM4ZlvmlTxAhCBdzHnEQWYiv7Mr4C3NeuVYhQYDPzOUUQgDLBQGcLHNhvQK8DACPx8PTxiqVyvISG43GbyaT6Qfpn06n0m63e/tPAPF4vJ1MJu8kEsnWTCkWi1yr1RKGw+GDRqPBOTfr44vFQvD7/Q/lcpmaaVQAr9fLp1IpO22c47hGOBz+MB6PH+Vy+VYDAL8qlUoGtVotzOfzq4MAgsHgE/6KojiQyWR/bKVSqbSszHFM8Pl8z1YK48JsNltCOBwOnrYLO+8AAIjb+nHbycoTiUQfDJ7tFq4YAHiVSmXBxcD41u8flQU8z7fhzO0r83atVns3Go3u9Xr9x0O/RQXo9/tsIBBg6vX606a52Wz+bZ7P5/WwG29gxSJzhKgA6XTaDoFNF+krFAocmC//4yWEcSf2wTm7mCO19xFgSsKOLI16vV7b7XY7mRNoLwA0JymJ5uQIzgIAuX5PzDElT2m+E8BqtQ4ymcx7Yq7T6a6ZE4sKgOadTucaCwkxp1UzlEKh0GDxIXOwDWHAdi6Xe3swQDQa/Q7mywoolUpvsaptymazDWKxmBHTlWXZm405BFZoNpuGgwEmk4mE2SGtVivii4f1AO7J3ZopkQCQj7Ar1FeRChCJRJzVapX6DKNIfSc1Ax+wtQWQ55h6bH8FWDfYV4fO3wlwDr0C/BcADYiTPCxHqIEA2QsCZAkAKnRGkMbKN/sTX5YHPQ1e7SkAAAAASUVORK5CYII=', - subscriptionStatus: 'incomplete', }, }; @@ -50,7 +43,6 @@ export const seedWorkspaces = async ( 'domainName', 'inviteHash', 'logo', - 'subscriptionStatus', ]) .orIgnore() .values(workspaces[workspaceId]) diff --git a/packages/twenty-server/src/database/typeorm/core/migrations/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts b/packages/twenty-server/src/database/typeorm/core/migrations/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts new file mode 100644 index 00000000000..85e64b408c4 --- /dev/null +++ b/packages/twenty-server/src/database/typeorm/core/migrations/1719494707738-removeSubscriptionStatusFromCoreWorkspace.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class RemoveSubscriptionStatusFromCoreWorkspace1719494707738 + implements MigrationInterface +{ + name = 'RemoveSubscriptionStatusFromCoreWorkspace1719494707738'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "core"."workspace" DROP COLUMN "subscriptionStatus"`, + ); + await queryRunner.query( + `DROP TYPE "core"."workspace_subscriptionstatus_enum"`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "core"."workspace_subscriptionstatus_enum" AS ENUM('active', 'canceled', 'incomplete', 'incomplete_expired', 'past_due', 'paused', 'trialing', 'unpaid')`, + ); + await queryRunner.query( + `ALTER TABLE "core"."workspace" ADD "subscriptionStatus" "core"."workspace_subscriptionstatus_enum" NOT NULL DEFAULT 'incomplete'`, + ); + } +} diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts index 68aae31ac7f..fca6f40dc5a 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts @@ -145,7 +145,8 @@ export class SignInUpService { assert( !this.environmentService.get('IS_BILLING_ENABLED') || - workspace.subscriptionStatus !== SubscriptionStatus.Incomplete, + workspace.currentBillingSubscription?.status !== + SubscriptionStatus.Incomplete, 'Workspace subscription status is incomplete', ForbiddenException, ); @@ -200,7 +201,6 @@ export class SignInUpService { displayName: '', domainName: '', inviteHash: v4(), - subscriptionStatus: SubscriptionStatus.Incomplete, }); const workspace = await this.workspaceRepository.save(workspaceToCreate); diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index f01bb83cafe..14de7386103 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -271,10 +271,6 @@ export class BillingService { return; } - await this.workspaceRepository.update(workspaceId, { - subscriptionStatus: data.object.status, - }); - await this.billingSubscriptionRepository.upsert( { workspaceId: workspaceId, diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/feature-flag.entity.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/feature-flag.entity.ts index 6214f0f7a84..4f3264baa09 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/feature-flag.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/feature-flag.entity.ts @@ -23,6 +23,7 @@ export enum FeatureFlagKeys { IsStripeIntegrationEnabled = 'IS_STRIPE_INTEGRATION_ENABLED', IsContactCreationForSentAndReceivedEmailsEnabled = 'IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED', IsGoogleCalendarSyncV2Enabled = 'IS_GOOGLE_CALENDAR_SYNC_V2_ENABLED', + IsFreeAccessEnabled = 'IS_FREE_ACCESS_ENABLED', } @Entity({ name: 'featureFlag', schema: 'core' }) diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts index a036cafee61..782431984c2 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts @@ -1,4 +1,5 @@ import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { OnboardingResolver } from 'src/engine/core-modules/onboarding/onboarding.resolver'; @@ -8,6 +9,7 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module'; import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module'; import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; +import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @Module({ imports: [ @@ -17,6 +19,7 @@ import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; KeyValuePairModule, EnvironmentModule, BillingModule, + TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), ], exports: [OnboardingService], providers: [OnboardingService, OnboardingResolver], diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index a9e2203b903..f4b8118507b 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -1,4 +1,7 @@ import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; + +import { Repository } from 'typeorm'; import { KeyValuePairService } from 'src/engine/core-modules/key-value-pair/key-value-pair.service'; import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum'; @@ -11,10 +14,15 @@ import { ConnectedAccountRepository } from 'src/modules/connected-account/reposi import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; -import { BillingService } from 'src/engine/core-modules/billing/billing.service'; import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { isDefined } from 'src/utils/is-defined'; +import { + FeatureFlagEntity, + FeatureFlagKeys, +} from 'src/engine/core-modules/feature-flag/feature-flag.entity'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; enum OnboardingStepValues { SKIPPED = 'SKIPPED', @@ -42,12 +50,32 @@ export class OnboardingService { private readonly connectedAccountRepository: ConnectedAccountRepository, @InjectWorkspaceRepository(WorkspaceMemberWorkspaceEntity) private readonly workspaceMemberRepository: WorkspaceRepository, + @InjectRepository(FeatureFlagEntity, 'core') + private readonly featureFlagRepository: Repository, ) {} private async isSubscriptionIncompleteOnboardingStatus(user: User) { + const isFreeAccessEnabled = await this.featureFlagRepository.findOneBy({ + workspaceId: user.defaultWorkspaceId, + key: FeatureFlagKeys.IsFreeAccessEnabled, + value: true, + }); + + if ( + isFreeAccessEnabled || + !this.environmentService.get('IS_BILLING_ENABLED') + ) { + return false; + } + + const currentBillingSubscription = + await this.billingService.getCurrentBillingSubscription({ + workspaceId: user.defaultWorkspaceId, + }); + return ( - this.environmentService.get('IS_BILLING_ENABLED') && - user.defaultWorkspace.subscriptionStatus === SubscriptionStatus.Incomplete + !isDefined(currentBillingSubscription) || + currentBillingSubscription?.status === SubscriptionStatus.Incomplete ); } diff --git a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts index 42e6a0d4fa6..bccb5f2cf62 100644 --- a/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts +++ b/packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts @@ -10,14 +10,10 @@ import { Relation, UpdateDateColumn, } from 'typeorm'; -import Stripe from 'stripe'; import { User } from 'src/engine/core-modules/user/user.entity'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { - BillingSubscription, - SubscriptionStatus, -} from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { UserWorkspace } from 'src/engine/core-modules/user-workspace/user-workspace.entity'; import { AppToken } from 'src/engine/core-modules/app-token/app-token.entity'; import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars'; @@ -88,15 +84,6 @@ export class Workspace { @OneToMany(() => FeatureFlagEntity, (featureFlag) => featureFlag.workspace) featureFlags: Relation; - @Field(() => SubscriptionStatus) - @Column({ - type: 'enum', - enum: Object.values(SubscriptionStatus), - default: SubscriptionStatus.Incomplete, - nullable: false, - }) - subscriptionStatus: Stripe.Subscription.Status; - @Field({ nullable: true }) currentBillingSubscription: BillingSubscription; diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts index 4b2252a2b3c..fb5faa313a1 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-cleaner/commands/delete-incomplete-workspaces.command.ts @@ -53,7 +53,7 @@ export class DeleteIncompleteWorkspacesCommand extends CommandRunner { options: DeleteIncompleteWorkspacesCommandOptions, ): Promise { const where: FindOptionsWhere = { - subscriptionStatus: SubscriptionStatus.Incomplete, + currentBillingSubscription: { status: SubscriptionStatus.Incomplete }, }; if (options.workspaceIds) { diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command.ts index d89a8357ce4..804759f55eb 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-sync-metadata/commands/add-standard-id.command.ts @@ -60,6 +60,7 @@ export class AddStandardIdCommand extends CommandRunner { IS_STRIPE_INTEGRATION_ENABLED: false, IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED: true, IS_GOOGLE_CALENDAR_SYNC_V2_ENABLED: true, + IS_FREE_ACCESS_ENABLED: false, }, ); const standardFieldMetadataCollection = this.standardFieldFactory.create( @@ -76,6 +77,7 @@ export class AddStandardIdCommand extends CommandRunner { IS_STRIPE_INTEGRATION_ENABLED: false, IS_CONTACT_CREATION_FOR_SENT_AND_RECEIVED_EMAILS_ENABLED: true, IS_GOOGLE_CALENDAR_SYNC_V2_ENABLED: true, + IS_FREE_ACCESS_ENABLED: false, }, ); diff --git a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts index b86f1453f83..c1e91c142b0 100644 --- a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts +++ b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts @@ -32,11 +32,13 @@ export class GoogleCalendarSyncCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), + currentBillingSubscription: { + status: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), + }, } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts index bd7c99b1449..a8dadbcd6a9 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts @@ -45,11 +45,13 @@ export class MessagingMessageListFetchCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), + currentBillingSubscription: { + status: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), + }, } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts index f578227b58d..069c1ac2da4 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts @@ -45,11 +45,13 @@ export class MessagingMessagesImportCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), + currentBillingSubscription: { + status: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), + }, } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts index 4e082ff1c67..2bce9b066b4 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts @@ -34,11 +34,13 @@ export class MessagingOngoingStaleCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), + currentBillingSubscription: { + status: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), + }, } : {}, select: ['id'], diff --git a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts index 63cacdec7a4..4e35795609b 100644 --- a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts +++ b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts @@ -46,11 +46,13 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob { await this.workspaceRepository.find({ where: this.environmentService.get('IS_BILLING_ENABLED') ? { - subscriptionStatus: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), + currentBillingSubscription: { + status: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), + }, } : {}, select: ['id'], From 9abc5e253857c298843ebdc05919368ec6e98cb1 Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 27 Jun 2024 16:18:58 +0200 Subject: [PATCH 38/47] Fix ci --- packages/twenty-front/src/testing/mock-data/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/twenty-front/src/testing/mock-data/config.ts b/packages/twenty-front/src/testing/mock-data/config.ts index d85622bd190..a14ea35bf85 100644 --- a/packages/twenty-front/src/testing/mock-data/config.ts +++ b/packages/twenty-front/src/testing/mock-data/config.ts @@ -38,4 +38,5 @@ export const mockedClientConfig: ClientConfig = { siteKey: 'MOCKED_SITE_KEY', __typename: 'Captcha', }, + api: { mutationMaximumAffectedRecords: 100 }, }; From 5a40bc413a8ef7846c4133e50708928b4b7f2567 Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 27 Jun 2024 16:57:24 +0200 Subject: [PATCH 39/47] Accept invite on all active workspaces --- .../src/engine/core-modules/auth/auth.module.ts | 2 ++ .../core-modules/auth/services/sign-in-up.service.ts | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts index 6a4d1c9d267..53953bc94b2 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/auth.module.ts @@ -30,6 +30,7 @@ import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/stan import { OnboardingModule } from 'src/engine/core-modules/onboarding/onboarding.module'; import { TwentyORMModule } from 'src/engine/twenty-orm/twenty-orm.module'; import { WorkspaceDataSourceModule } from 'src/engine/workspace-datasource/workspace-datasource.module'; +import { WorkspaceModule } from 'src/engine/core-modules/workspace/workspace.module'; import { AuthResolver } from './auth.resolver'; @@ -65,6 +66,7 @@ const jwtModule = JwtModule.registerAsync({ ]), HttpModule, UserWorkspaceModule, + WorkspaceModule, OnboardingModule, TwentyORMModule.forFeature([CalendarChannelWorkspaceEntity]), WorkspaceDataSourceModule, diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts index fca6f40dc5a..4dafcf12666 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts @@ -12,7 +12,6 @@ import FileType from 'file-type'; import { FileFolder } from 'src/engine/core-modules/file/interfaces/file-folder.interface'; -import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { assert } from 'src/utils/assert'; import { PASSWORD_REGEX, @@ -25,6 +24,7 @@ import { FileUploadService } from 'src/engine/core-modules/file/file-upload/serv import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { getImageBufferFromUrl } from 'src/utils/image'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; +import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service'; export type SignInUpServiceInput = { email: string; @@ -45,6 +45,7 @@ export class SignInUpService { @InjectRepository(User, 'core') private readonly userRepository: Repository, private readonly userWorkspaceService: UserWorkspaceService, + private readonly workspaceService: WorkspaceService, private readonly httpService: HttpService, private readonly environmentService: EnvironmentService, ) {} @@ -143,11 +144,12 @@ export class SignInUpService { ForbiddenException, ); + const isWorkspaceActivated = + await this.workspaceService.isWorkspaceActivated(workspace.id); + assert( - !this.environmentService.get('IS_BILLING_ENABLED') || - workspace.currentBillingSubscription?.status !== - SubscriptionStatus.Incomplete, - 'Workspace subscription status is incomplete', + isWorkspaceActivated, + 'Workspace is not ready to welcome new members', ForbiddenException, ); From 4e45a0f1dad40af3463addd512ca31bcf7ca278c Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 27 Jun 2024 17:21:28 +0200 Subject: [PATCH 40/47] Fix ci --- .../core-modules/auth/services/sign-in-up.service.spec.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts index 370bfeee898..b6b381ec0d1 100644 --- a/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts +++ b/packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.spec.ts @@ -8,6 +8,7 @@ import { EnvironmentService } from 'src/engine/integrations/environment/environm import { SignInUpService } from 'src/engine/core-modules/auth/services/sign-in-up.service'; import { FileUploadService } from 'src/engine/core-modules/file/file-upload/services/file-upload.service'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; +import { WorkspaceService } from 'src/engine/core-modules/workspace/services/workspace.service'; describe('SignInUpService', () => { let service: SignInUpService; @@ -40,6 +41,10 @@ describe('SignInUpService', () => { provide: HttpService, useValue: {}, }, + { + provide: WorkspaceService, + useValue: {}, + }, ], }).compile(); From 9eaa2c32d368a6e311eb292b29e720b5d90fd5bb Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 27 Jun 2024 17:58:21 +0200 Subject: [PATCH 41/47] Use isFreeAccessEnabled when necessary --- .../core-modules/billing/billing.module.ts | 8 ++++++- .../core-modules/billing/billing.service.ts | 18 +++++++++++++++ .../billing-workspace-member.listener.ts | 11 ++++++--- .../onboarding/onboarding.module.ts | 3 --- .../onboarding/onboarding.service.ts | 23 ++++--------------- 5 files changed, 38 insertions(+), 25 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts index 4be921b78c0..491faaec7e2 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.module.ts @@ -10,13 +10,19 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { BillingResolver } from 'src/engine/core-modules/billing/billing.resolver'; import { BillingWorkspaceMemberListener } from 'src/engine/core-modules/billing/listeners/billing-workspace-member.listener'; import { UserWorkspaceModule } from 'src/engine/core-modules/user-workspace/user-workspace.module'; +import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @Module({ imports: [ StripeModule, UserWorkspaceModule, TypeOrmModule.forFeature( - [BillingSubscription, BillingSubscriptionItem, Workspace], + [ + BillingSubscription, + BillingSubscriptionItem, + Workspace, + FeatureFlagEntity, + ], 'core', ), ], diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index 14de7386103..c3d475dba38 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -17,6 +17,10 @@ import { ProductPriceEntity } from 'src/engine/core-modules/billing/dto/product- import { User } from 'src/engine/core-modules/user/user.entity'; import { assert } from 'src/utils/assert'; import { UserWorkspaceService } from 'src/engine/core-modules/user-workspace/user-workspace.service'; +import { + FeatureFlagEntity, + FeatureFlagKeys, +} from 'src/engine/core-modules/feature-flag/feature-flag.entity'; export enum AvailableProduct { BasePlan = 'base-plan', @@ -38,12 +42,26 @@ export class BillingService { private readonly environmentService: EnvironmentService, @InjectRepository(BillingSubscription, 'core') private readonly billingSubscriptionRepository: Repository, + @InjectRepository(FeatureFlagEntity, 'core') + private readonly featureFlagRepository: Repository, @InjectRepository(BillingSubscriptionItem, 'core') private readonly billingSubscriptionItemRepository: Repository, @InjectRepository(Workspace, 'core') private readonly workspaceRepository: Repository, ) {} + async isBillingEnabledForWorkspace(workspaceId: string) { + const isFreeAccessEnabled = await this.featureFlagRepository.findOneBy({ + workspaceId, + key: FeatureFlagKeys.IsFreeAccessEnabled, + value: true, + }); + + return ( + !isFreeAccessEnabled && this.environmentService.get('IS_BILLING_ENABLED') + ); + } + getProductStripeId(product: AvailableProduct) { if (product === AvailableProduct.BasePlan) { return this.environmentService.get('BILLING_STRIPE_BASE_PLAN_PRODUCT_ID'); diff --git a/packages/twenty-server/src/engine/core-modules/billing/listeners/billing-workspace-member.listener.ts b/packages/twenty-server/src/engine/core-modules/billing/listeners/billing-workspace-member.listener.ts index ae945d309b8..32f70758f4a 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/listeners/billing-workspace-member.listener.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/listeners/billing-workspace-member.listener.ts @@ -9,15 +9,15 @@ import { UpdateSubscriptionJob, UpdateSubscriptionJobData, } from 'src/engine/core-modules/billing/jobs/update-subscription.job'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; @Injectable() export class BillingWorkspaceMemberListener { constructor( @InjectMessageQueue(MessageQueue.billingQueue) private readonly messageQueueService: MessageQueueService, - private readonly environmentService: EnvironmentService, + private readonly billingService: BillingService, ) {} @OnEvent('workspaceMember.created') @@ -25,7 +25,12 @@ export class BillingWorkspaceMemberListener { async handleCreateOrDeleteEvent( payload: ObjectRecordCreateEvent, ) { - if (!this.environmentService.get('IS_BILLING_ENABLED')) { + const isBillingEnabledForWorkspace = + await this.billingService.isBillingEnabledForWorkspace( + payload.workspaceId, + ); + + if (!isBillingEnabledForWorkspace) { return; } diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts index 782431984c2..a036cafee61 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.module.ts @@ -1,5 +1,4 @@ import { Module } from '@nestjs/common'; -import { TypeOrmModule } from '@nestjs/typeorm'; import { OnboardingService } from 'src/engine/core-modules/onboarding/onboarding.service'; import { OnboardingResolver } from 'src/engine/core-modules/onboarding/onboarding.resolver'; @@ -9,7 +8,6 @@ import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-s import { WorkspaceManagerModule } from 'src/engine/workspace-manager/workspace-manager.module'; import { EnvironmentModule } from 'src/engine/integrations/environment/environment.module'; import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; -import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; @Module({ imports: [ @@ -19,7 +17,6 @@ import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature- KeyValuePairModule, EnvironmentModule, BillingModule, - TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), ], exports: [OnboardingService], providers: [OnboardingService, OnboardingResolver], diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index f4b8118507b..61fe5a14872 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -1,7 +1,4 @@ import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; - -import { Repository } from 'typeorm'; import { KeyValuePairService } from 'src/engine/core-modules/key-value-pair/key-value-pair.service'; import { OnboardingStatus } from 'src/engine/core-modules/onboarding/enums/onboarding-status.enum'; @@ -18,10 +15,6 @@ import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inje import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; import { isDefined } from 'src/utils/is-defined'; -import { - FeatureFlagEntity, - FeatureFlagKeys, -} from 'src/engine/core-modules/feature-flag/feature-flag.entity'; import { BillingService } from 'src/engine/core-modules/billing/billing.service'; enum OnboardingStepValues { @@ -50,21 +43,15 @@ export class OnboardingService { private readonly connectedAccountRepository: ConnectedAccountRepository, @InjectWorkspaceRepository(WorkspaceMemberWorkspaceEntity) private readonly workspaceMemberRepository: WorkspaceRepository, - @InjectRepository(FeatureFlagEntity, 'core') - private readonly featureFlagRepository: Repository, ) {} private async isSubscriptionIncompleteOnboardingStatus(user: User) { - const isFreeAccessEnabled = await this.featureFlagRepository.findOneBy({ - workspaceId: user.defaultWorkspaceId, - key: FeatureFlagKeys.IsFreeAccessEnabled, - value: true, - }); + const isBillingEnabledForWorkspace = + await this.billingService.isBillingEnabledForWorkspace( + user.defaultWorkspaceId, + ); - if ( - isFreeAccessEnabled || - !this.environmentService.get('IS_BILLING_ENABLED') - ) { + if (!isBillingEnabledForWorkspace) { return false; } From 8ed51e0813bd5bd1770a9b1b1e404d6e139da397 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 28 Jun 2024 09:39:04 +0200 Subject: [PATCH 42/47] Factorize paying workspace ids --- .../core-modules/billing/billing.service.ts | 21 ++++++++++++++- .../crons/jobs/calendar-cron-job.module.ts | 5 ++-- .../jobs/google-calendar-sync.cron.job.ts | 26 +++---------------- .../messaging-message-list-fetch.cron.job.ts | 26 +++---------------- .../messaging-messages-import.cron.job.ts | 26 +++---------------- .../jobs/messaging-ongoing-stale.cron.job.ts | 26 +++---------------- .../messaging-import-manager.module.ts | 2 ++ ...age-channel-sync-status-monitoring.cron.ts | 23 +++------------- .../monitoring/messaging-monitoring.module.ts | 2 ++ 9 files changed, 47 insertions(+), 110 deletions(-) diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index c3d475dba38..db67853125f 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import Stripe from 'stripe'; -import { Not, Repository } from 'typeorm'; +import { In, Not, Repository } from 'typeorm'; import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { StripeService } from 'src/engine/core-modules/billing/stripe/stripe.service'; @@ -50,6 +50,25 @@ export class BillingService { private readonly workspaceRepository: Repository, ) {} + async getActiveSubscriptionWorkspaceIds() { + return ( + await this.workspaceRepository.find({ + where: this.environmentService.get('IS_BILLING_ENABLED') + ? { + currentBillingSubscription: { + status: In([ + SubscriptionStatus.Active, + SubscriptionStatus.Trialing, + SubscriptionStatus.PastDue, + ]), + }, + } + : {}, + select: ['id'], + }) + ).map((workspace) => workspace.id); + } + async isBillingEnabledForWorkspace(workspaceId: string) { const isFreeAccessEnabled = await this.featureFlagRepository.findOneBy({ workspaceId, diff --git a/packages/twenty-server/src/modules/calendar/crons/jobs/calendar-cron-job.module.ts b/packages/twenty-server/src/modules/calendar/crons/jobs/calendar-cron-job.module.ts index 2545118d5d7..38be6456d4f 100644 --- a/packages/twenty-server/src/modules/calendar/crons/jobs/calendar-cron-job.module.ts +++ b/packages/twenty-server/src/modules/calendar/crons/jobs/calendar-cron-job.module.ts @@ -2,16 +2,17 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { FeatureFlagEntity } from 'src/engine/core-modules/feature-flag/feature-flag.entity'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; import { GoogleCalendarSyncCronJob } from 'src/modules/calendar/crons/jobs/google-calendar-sync.cron.job'; import { WorkspaceGoogleCalendarSyncModule } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.module'; +import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; @Module({ imports: [ - TypeOrmModule.forFeature([Workspace, FeatureFlagEntity], 'core'), + TypeOrmModule.forFeature([FeatureFlagEntity], 'core'), TypeOrmModule.forFeature([DataSourceEntity], 'metadata'), WorkspaceGoogleCalendarSyncModule, + BillingModule, ], providers: [GoogleCalendarSyncCronJob], }) diff --git a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts index c1e91c142b0..40b56c1b4f8 100644 --- a/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts +++ b/packages/twenty-server/src/modules/calendar/crons/jobs/google-calendar-sync.cron.job.ts @@ -3,14 +3,12 @@ import { Scope } from '@nestjs/common'; import { Repository, In } from 'typeorm'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; import { WorkspaceGoogleCalendarSyncService } from 'src/modules/calendar/services/workspace-google-calendar-sync/workspace-google-calendar-sync.service'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants'; import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator'; import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; -import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; @Processor({ queueName: MessageQueue.cronQueue, @@ -18,32 +16,16 @@ import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/bil }) export class GoogleCalendarSyncCronJob { constructor( - @InjectRepository(Workspace, 'core') - private readonly workspaceRepository: Repository, @InjectRepository(DataSourceEntity, 'metadata') private readonly dataSourceRepository: Repository, private readonly workspaceGoogleCalendarSyncService: WorkspaceGoogleCalendarSyncService, - private readonly environmentService: EnvironmentService, + private readonly billingService: BillingService, ) {} @Process(GoogleCalendarSyncCronJob.name) async handle(): Promise { - const workspaceIds = ( - await this.workspaceRepository.find({ - where: this.environmentService.get('IS_BILLING_ENABLED') - ? { - currentBillingSubscription: { - status: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), - }, - } - : {}, - select: ['id'], - }) - ).map((workspace) => workspace.id); + const workspaceIds = + await this.billingService.getActiveSubscriptionWorkspaceIds(); const dataSources = await this.dataSourceRepository.find({ where: { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts index a8dadbcd6a9..0e8811b6b14 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-message-list-fetch.cron.job.ts @@ -3,12 +3,10 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository, In } from 'typeorm'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { MessageChannelRepository } from 'src/modules/messaging/common/repositories/message-channel.repository'; import { MessageChannelSyncStage, @@ -21,42 +19,26 @@ import { import { InjectMessageQueue } from 'src/engine/integrations/message-queue/decorators/message-queue.decorator'; import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator'; import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; -import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; @Processor(MessageQueue.cronQueue) export class MessagingMessageListFetchCronJob { private readonly logger = new Logger(MessagingMessageListFetchCronJob.name); constructor( - @InjectRepository(Workspace, 'core') - private readonly workspaceRepository: Repository, @InjectRepository(DataSourceEntity, 'metadata') private readonly dataSourceRepository: Repository, @InjectMessageQueue(MessageQueue.messagingQueue) private readonly messageQueueService: MessageQueueService, @InjectObjectMetadataRepository(MessageChannelWorkspaceEntity) private readonly messageChannelRepository: MessageChannelRepository, - private readonly environmentService: EnvironmentService, + private readonly billingService: BillingService, ) {} @Process(MessagingMessageListFetchCronJob.name) async handle(): Promise { - const workspaceIds = ( - await this.workspaceRepository.find({ - where: this.environmentService.get('IS_BILLING_ENABLED') - ? { - currentBillingSubscription: { - status: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), - }, - } - : {}, - select: ['id'], - }) - ).map((workspace) => workspace.id); + const workspaceIds = + await this.billingService.getActiveSubscriptionWorkspaceIds(); const dataSources = await this.dataSourceRepository.find({ where: { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts index 069c1ac2da4..4be6ce2e121 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-messages-import.cron.job.ts @@ -3,9 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository, In } from 'typeorm'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants'; import { MessageQueueService } from 'src/engine/integrations/message-queue/services/message-queue.service'; import { @@ -21,42 +19,26 @@ import { MessageChannelSyncStage, MessageChannelWorkspaceEntity, } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; -import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; @Processor(MessageQueue.cronQueue) export class MessagingMessagesImportCronJob { private readonly logger = new Logger(MessagingMessagesImportCronJob.name); constructor( - @InjectRepository(Workspace, 'core') - private readonly workspaceRepository: Repository, @InjectRepository(DataSourceEntity, 'metadata') private readonly dataSourceRepository: Repository, - private readonly environmentService: EnvironmentService, @InjectMessageQueue(MessageQueue.messagingQueue) private readonly messageQueueService: MessageQueueService, @InjectObjectMetadataRepository(MessageChannelWorkspaceEntity) private readonly messageChannelRepository: MessageChannelRepository, + private readonly billingService: BillingService, ) {} @Process(MessagingMessagesImportCronJob.name) async handle(): Promise { - const workspaceIds = ( - await this.workspaceRepository.find({ - where: this.environmentService.get('IS_BILLING_ENABLED') - ? { - currentBillingSubscription: { - status: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), - }, - } - : {}, - select: ['id'], - }) - ).map((workspace) => workspace.id); + const workspaceIds = + await this.billingService.getActiveSubscriptionWorkspaceIds(); const dataSources = await this.dataSourceRepository.find({ where: { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts index 2bce9b066b4..bc1bce31ce1 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/crons/jobs/messaging-ongoing-stale.cron.job.ts @@ -2,9 +2,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository, In } from 'typeorm'; -import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants'; import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator'; import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; @@ -14,38 +12,22 @@ import { MessagingOngoingStaleJobData, MessagingOngoingStaleJob, } from 'src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job'; -import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; @Processor(MessageQueue.cronQueue) export class MessagingOngoingStaleCronJob { constructor( - @InjectRepository(Workspace, 'core') - private readonly workspaceRepository: Repository, @InjectRepository(DataSourceEntity, 'metadata') private readonly dataSourceRepository: Repository, - private readonly environmentService: EnvironmentService, @InjectMessageQueue(MessageQueue.messagingQueue) private readonly messageQueueService: MessageQueueService, + private readonly billingService: BillingService, ) {} @Process(MessagingOngoingStaleCronJob.name) async handle(): Promise { - const workspaceIds = ( - await this.workspaceRepository.find({ - where: this.environmentService.get('IS_BILLING_ENABLED') - ? { - currentBillingSubscription: { - status: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), - }, - } - : {}, - select: ['id'], - }) - ).map((workspace) => workspace.id); + const workspaceIds = + await this.billingService.getActiveSubscriptionWorkspaceIds(); const dataSources = await this.dataSourceRepository.find({ where: { diff --git a/packages/twenty-server/src/modules/messaging/message-import-manager/messaging-import-manager.module.ts b/packages/twenty-server/src/modules/messaging/message-import-manager/messaging-import-manager.module.ts index 11800472aa6..4eea3c0d454 100644 --- a/packages/twenty-server/src/modules/messaging/message-import-manager/messaging-import-manager.module.ts +++ b/packages/twenty-server/src/modules/messaging/message-import-manager/messaging-import-manager.module.ts @@ -20,6 +20,7 @@ import { MessagingMessageListFetchJob } from 'src/modules/messaging/message-impo import { MessagingMessagesImportJob } from 'src/modules/messaging/message-import-manager/jobs/messaging-messages-import.job'; import { MessagingOngoingStaleJob } from 'src/modules/messaging/message-import-manager/jobs/messaging-ongoing-stale.job'; import { MessagingMessageImportManagerMessageChannelListener } from 'src/modules/messaging/message-import-manager/listeners/messaging-import-manager-message-channel.listener'; +import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; @Module({ imports: [ @@ -28,6 +29,7 @@ import { MessagingMessageImportManagerMessageChannelListener } from 'src/modules TypeOrmModule.forFeature([Workspace], 'core'), TypeOrmModule.forFeature([DataSourceEntity], 'metadata'), TwentyORMModule.forFeature([MessageChannelWorkspaceEntity]), + BillingModule, ], providers: [ MessagingMessageListFetchCronCommand, diff --git a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts index 4e35795609b..a0384d52829 100644 --- a/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts +++ b/packages/twenty-server/src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron.ts @@ -8,13 +8,12 @@ import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-source.entity'; import { MessageQueue } from 'src/engine/integrations/message-queue/message-queue.constants'; import { InjectObjectMetadataRepository } from 'src/engine/object-metadata-repository/object-metadata-repository.decorator'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { MessageChannelRepository } from 'src/modules/messaging/common/repositories/message-channel.repository'; import { MessageChannelWorkspaceEntity } from 'src/modules/messaging/common/standard-objects/message-channel.workspace-entity'; import { Processor } from 'src/engine/integrations/message-queue/decorators/processor.decorator'; import { Process } from 'src/engine/integrations/message-queue/decorators/process.decorator'; import { MessagingTelemetryService } from 'src/modules/messaging/common/services/messaging-telemetry.service'; -import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; +import { BillingService } from 'src/engine/core-modules/billing/billing.service'; @Processor(MessageQueue.cronQueue) export class MessagingMessageChannelSyncStatusMonitoringCronJob { @@ -29,7 +28,7 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob { private readonly dataSourceRepository: Repository, @InjectObjectMetadataRepository(MessageChannelWorkspaceEntity) private readonly messageChannelRepository: MessageChannelRepository, - private readonly environmentService: EnvironmentService, + private readonly billingService: BillingService, private readonly messagingTelemetryService: MessagingTelemetryService, ) {} @@ -42,22 +41,8 @@ export class MessagingMessageChannelSyncStatusMonitoringCronJob { message: 'Starting message channel sync status monitoring', }); - const workspaceIds = ( - await this.workspaceRepository.find({ - where: this.environmentService.get('IS_BILLING_ENABLED') - ? { - currentBillingSubscription: { - status: In([ - SubscriptionStatus.Active, - SubscriptionStatus.Trialing, - SubscriptionStatus.PastDue, - ]), - }, - } - : {}, - select: ['id'], - }) - ).map((workspace) => workspace.id); + const workspaceIds = + await this.billingService.getActiveSubscriptionWorkspaceIds(); const dataSources = await this.dataSourceRepository.find({ where: { diff --git a/packages/twenty-server/src/modules/messaging/monitoring/messaging-monitoring.module.ts b/packages/twenty-server/src/modules/messaging/monitoring/messaging-monitoring.module.ts index 2d6c04c7544..d41ed75e413 100644 --- a/packages/twenty-server/src/modules/messaging/monitoring/messaging-monitoring.module.ts +++ b/packages/twenty-server/src/modules/messaging/monitoring/messaging-monitoring.module.ts @@ -6,10 +6,12 @@ import { DataSourceEntity } from 'src/engine/metadata-modules/data-source/data-s import { MessagingCommonModule } from 'src/modules/messaging/common/messaging-common.module'; import { MessagingMessageChannelSyncStatusMonitoringCronCommand } from 'src/modules/messaging/monitoring/crons/commands/messaging-message-channel-sync-status-monitoring.cron.command'; import { MessagingMessageChannelSyncStatusMonitoringCronJob } from 'src/modules/messaging/monitoring/crons/jobs/messaging-message-channel-sync-status-monitoring.cron'; +import { BillingModule } from 'src/engine/core-modules/billing/billing.module'; @Module({ imports: [ MessagingCommonModule, + BillingModule, TypeOrmModule.forFeature([Workspace], 'core'), TypeOrmModule.forFeature([DataSourceEntity], 'metadata'), ], From fc4e75f368fed7d57eae85d8d2ace6b3cf6cdd12 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 28 Jun 2024 10:47:58 +0200 Subject: [PATCH 43/47] Remove IsFreeAccessEnabled feature flag when subscription created --- .../src/engine/core-modules/billing/billing.service.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts index db67853125f..8a391b06c61 100644 --- a/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts +++ b/packages/twenty-server/src/engine/core-modules/billing/billing.service.ts @@ -341,5 +341,10 @@ export class BillingService { skipUpdateIfNoValuesChanged: true, }, ); + + await this.featureFlagRepository.delete({ + workspaceId, + key: FeatureFlagKeys.IsFreeAccessEnabled, + }); } } From 959c8ca3225c1f6000841bc5b71b6482b4be7994 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 28 Jun 2024 15:34:20 +0200 Subject: [PATCH 44/47] Fixes --- .../usePageChangeEffectNavigateLocation.test.ts | 2 +- .../src/modules/ui/display/info/components/Info.tsx | 1 + .../twenty-front/src/pages/onboarding/PaymentSuccess.tsx | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index c42ce0d13dd..24724c15e8b 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -41,7 +41,7 @@ const defaultHomePagePath = '/objects/companies'; jest.mock('~/hooks/useDefaultHomePagePath'); jest.mocked(useDefaultHomePagePath).mockReturnValue({ - defaultHomePagePath: '/objects/companies', + defaultHomePagePath, }); // prettier-ignore diff --git a/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx b/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx index 44f61dd057a..948dedda4e6 100644 --- a/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx +++ b/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx @@ -30,6 +30,7 @@ const StyledInfo = styled.div>` font-weight: ${({ theme }) => theme.font.weight.medium}; justify-content: space-between; max-width: 512px; + gap: ${({ theme }) => theme.spacing(2)}; padding: ${({ theme }) => theme.spacing(2)}; ${({ theme, accent }) => { switch (accent) { diff --git a/packages/twenty-front/src/pages/onboarding/PaymentSuccess.tsx b/packages/twenty-front/src/pages/onboarding/PaymentSuccess.tsx index f77d7360e65..ce57c50795a 100644 --- a/packages/twenty-front/src/pages/onboarding/PaymentSuccess.tsx +++ b/packages/twenty-front/src/pages/onboarding/PaymentSuccess.tsx @@ -1,14 +1,17 @@ import React from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { useRecoilValue } from 'recoil'; import { IconCheck, RGBA } from 'twenty-ui'; import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; +import { currentUserState } from '@/auth/states/currentUserState'; import { AppPath } from '@/types/AppPath'; import { MainButton } from '@/ui/input/button/components/MainButton'; import { UndecoratedLink } from '@/ui/navigation/link/components/UndecoratedLink'; import { AnimatedEaseIn } from '@/ui/utilities/animation/components/AnimatedEaseIn'; +import { OnboardingStatus } from '~/generated/graphql'; const StyledCheckContainer = styled.div` align-items: center; @@ -29,8 +32,14 @@ const StyledButtonContainer = styled.div` export const PaymentSuccess = () => { const theme = useTheme(); + const currentUser = useRecoilValue(currentUserState); const color = theme.name === 'light' ? theme.grayScale.gray90 : theme.grayScale.gray10; + + if (currentUser?.onboardingStatus === OnboardingStatus.Completed) { + return <>; + } + return ( <> From 8ff17d9f4ec1503479c448b2c1f7e7a06424e924 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 28 Jun 2024 16:02:45 +0200 Subject: [PATCH 45/47] Code review return, remove onboardingStatus lie --- ...sePageChangeEffectNavigateLocation.test.ts | 436 ++++++++-------- .../usePageChangeEffectNavigateLocation.ts | 20 +- .../onboarding/hooks/useOnboardingStatus.ts | 5 - .../hooks/__tests__/useShowAuthModal.test.tsx | 476 +++++++++--------- .../ui/layout/hooks/useShowAuthModal.ts | 11 +- .../workspace/hooks/useSubscriptionStatus.ts | 20 +- .../hooks/useWorkspaceHasSubscription.ts | 10 - .../src/pages/settings/SettingsBilling.tsx | 4 +- .../onboarding/onboarding.service.ts | 2 - 9 files changed, 469 insertions(+), 515 deletions(-) delete mode 100644 packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 24724c15e8b..657529c191e 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -1,7 +1,7 @@ +import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; -import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; @@ -21,13 +21,6 @@ const setupMockSubscriptionStatus = ( jest.mocked(useSubscriptionStatus).mockReturnValueOnce(subscriptionStatus); }; -jest.mock('@/workspace/hooks/useWorkspaceHasSubscription'); -const setupMockWorkspaceHasSubscription = (workspaceHasSubscription = true) => { - jest - .mocked(useWorkspaceHasSubscription) - .mockReturnValueOnce(workspaceHasSubscription); -}; - jest.mock('~/hooks/useIsMatchingLocation'); const mockUseIsMatchingLocation = jest.mocked(useIsMatchingLocation); @@ -37,6 +30,11 @@ const setupMockIsMatchingLocation = (pathname: string) => { ); }; +jest.mock('@/auth/hooks/useIsLogged'); +const setupMockIsLogged = (isLogged: boolean) => { + jest.mocked(useIsLogged).mockReturnValueOnce(isLogged); +}; + const defaultHomePagePath = '/objects/companies'; jest.mock('~/hooks/useDefaultHomePagePath'); @@ -46,236 +44,236 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({ // prettier-ignore const testCases = [ - { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Verify, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SignInUp, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateWorkspace, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.CreateProfile, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.SyncEmails, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.InviteTeam, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined }, + { loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequired, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, + { loc: AppPath.Index, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath }, - { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.TasksPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordIndexPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.RecordShowPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Impersonate, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Impersonate, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.Authorize, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, - { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined }, + { loc: AppPath.NotFound, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam }, + { loc: AppPath.NotFound, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined }, ]; describe('usePageChangeEffectNavigateLocation', () => { @@ -284,7 +282,7 @@ describe('usePageChangeEffectNavigateLocation', () => { setupMockIsMatchingLocation(testCase.loc); setupMockOnboardingStatus(testCase.onboardingStatus); setupMockSubscriptionStatus(testCase.subscriptionStatus); - setupMockWorkspaceHasSubscription(); + setupMockIsLogged(testCase.isLoggedIn); expect(usePageChangeEffectNavigateLocation()).toEqual(testCase.res); }); }); diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts index 00f0ad075f3..5492b7b988a 100644 --- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts +++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts @@ -1,17 +1,18 @@ +import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { SettingsPath } from '@/types/SettingsPath'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; -import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useDefaultHomePagePath } from '~/hooks/useDefaultHomePagePath'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; +import { isDefined } from '~/utils/isDefined'; export const usePageChangeEffectNavigateLocation = () => { const isMatchingLocation = useIsMatchingLocation(); + const isLoggedIn = useIsLogged(); const onboardingStatus = useOnboardingStatus(); const subscriptionStatus = useSubscriptionStatus(); - const workspaceHasSubscription = useWorkspaceHasSubscription(); const { defaultHomePagePath } = useDefaultHomePagePath(); const isMatchingOpenRoute = @@ -36,7 +37,7 @@ export const usePageChangeEffectNavigateLocation = () => { return; } - if (!onboardingStatus && !isMatchingOngoingUserCreationRoute) { + if (!isLoggedIn && !isMatchingOngoingUserCreationRoute) { return AppPath.SignInUp; } @@ -99,18 +100,9 @@ export const usePageChangeEffectNavigateLocation = () => { if ( onboardingStatus === OnboardingStatus.Completed && - workspaceHasSubscription && - subscriptionStatus !== SubscriptionStatus.Canceled && - isMatchingOnboardingRoute - ) { - return defaultHomePagePath; - } - - if ( - onboardingStatus === OnboardingStatus.Completed && - !workspaceHasSubscription && isMatchingOnboardingRoute && - !isMatchingLocation(AppPath.PlanRequired) + subscriptionStatus !== SubscriptionStatus.Canceled && + (isDefined(subscriptionStatus) || !isMatchingLocation(AppPath.PlanRequired)) ) { return defaultHomePagePath; } diff --git a/packages/twenty-front/src/modules/onboarding/hooks/useOnboardingStatus.ts b/packages/twenty-front/src/modules/onboarding/hooks/useOnboardingStatus.ts index 3e4828fc93b..28bef7fd4a5 100644 --- a/packages/twenty-front/src/modules/onboarding/hooks/useOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/onboarding/hooks/useOnboardingStatus.ts @@ -1,14 +1,9 @@ import { useRecoilValue } from 'recoil'; -import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { currentUserState } from '@/auth/states/currentUserState'; import { OnboardingStatus } from '~/generated/graphql'; export const useOnboardingStatus = (): OnboardingStatus | null | undefined => { const currentUser = useRecoilValue(currentUserState); - const isLoggedIn = useIsLogged(); - if (!isLoggedIn) { - return undefined; - } return currentUser?.onboardingStatus; }; diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx index 780b26dd670..270ab80b1aa 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx +++ b/packages/twenty-front/src/modules/ui/layout/hooks/__tests__/useShowAuthModal.test.tsx @@ -1,12 +1,12 @@ import { renderHook } from '@testing-library/react'; import { RecoilRoot, useSetRecoilState } from 'recoil'; +import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { useShowAuthModal } from '@/ui/layout/hooks/useShowAuthModal'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; -import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; @@ -24,11 +24,9 @@ const setupMockSubscriptionStatus = ( jest.mocked(useSubscriptionStatus).mockReturnValueOnce(subscriptionStatus); }; -jest.mock('@/workspace/hooks/useWorkspaceHasSubscription'); -const setupMockWorkspaceHasSubscription = (workspaceHasSubscription = true) => { - jest - .mocked(useWorkspaceHasSubscription) - .mockReturnValueOnce(workspaceHasSubscription); +jest.mock('@/auth/hooks/useIsLogged'); +const setupMockIsLogged = (isLogged: boolean) => { + jest.mocked(useIsLogged).mockReturnValueOnce(isLogged); }; jest.mock('~/hooks/useIsMatchingLocation'); @@ -57,236 +55,236 @@ const getResult = (isDefaultLayoutAuthModalVisible = true) => // prettier-ignore const testCases = [ - { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Verify, subscriptionStatus: undefined, onboardingStatus: undefined, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: false }, - { loc: AppPath.Verify, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SignInUp, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.SignInUp, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: true }, - { loc: AppPath.Invite, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Invite, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: true }, - - { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.ResetPassword, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: true }, - - { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.CreateWorkspace, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.CreateProfile, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.CreateProfile, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SyncEmails, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.SyncEmails, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.InviteTeam, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.InviteTeam, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequired, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.PlanRequired, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.PlanRequiredSuccess, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Index, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Index, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.TasksPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.TasksPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.OpportunitiesPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.RecordIndexPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.RecordShowPage, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.RecordShowPage, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.SettingsCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.DevelopersCatchAll, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Impersonate, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Impersonate, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.Authorize, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.Authorize, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.NotFoundWildcard, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, - - { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, - { loc: AppPath.NotFound, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, - { loc: AppPath.NotFound, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Verify, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: false }, + { loc: AppPath.Verify, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SignInUp, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SignInUp, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.Invite, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Invite, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: true }, + + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.ResetPassword, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.ResetPassword, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: true }, + + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateWorkspace, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.CreateWorkspace, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.CreateProfile, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.CreateProfile, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SyncEmails, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SyncEmails, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.InviteTeam, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.InviteTeam, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: true }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequired, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.PlanRequired, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.PlanRequiredSuccess, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.PlanRequiredSuccess, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Index, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Index, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Index, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.TasksPage, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.TasksPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.OpportunitiesPage, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.OpportunitiesPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordIndexPage, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.RecordIndexPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.RecordShowPage, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.RecordShowPage, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.SettingsCatchAll, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.SettingsCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.DevelopersCatchAll, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.DevelopersCatchAll, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Impersonate, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Impersonate, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.Authorize, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.Authorize, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFoundWildcard, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.NotFoundWildcard, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, + + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: true }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: false }, + { loc: AppPath.NotFound, isLogged: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: true }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: true }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: true }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: true }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: true }, + { loc: AppPath.NotFound, isLogged: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: false }, ]; describe('useShowAuthModal', () => { @@ -295,7 +293,7 @@ describe('useShowAuthModal', () => { setupMockOnboardingStatus(testCase.onboardingStatus); setupMockSubscriptionStatus(testCase.subscriptionStatus); setupMockIsMatchingLocation(testCase.loc); - setupMockWorkspaceHasSubscription(); + setupMockIsLogged(testCase.isLogged); const { result } = getResult(); if (testCase.res) { expect(result.current).toBeTruthy(); @@ -310,7 +308,7 @@ describe('useShowAuthModal', () => { setupMockOnboardingStatus(OnboardingStatus.Completed); setupMockSubscriptionStatus(SubscriptionStatus.Active); setupMockIsMatchingLocation(AppPath.Invite); - setupMockWorkspaceHasSubscription(); + setupMockIsLogged(true); const { result } = getResult(false); expect(result.current).toBeFalsy(); }); @@ -318,7 +316,7 @@ describe('useShowAuthModal', () => { setupMockOnboardingStatus(OnboardingStatus.Completed); setupMockSubscriptionStatus(SubscriptionStatus.Active); setupMockIsMatchingLocation(AppPath.ResetPassword); - setupMockWorkspaceHasSubscription(); + setupMockIsLogged(true); const { result } = getResult(false); expect(result.current).toBeFalsy(); }); diff --git a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts index be4ea591936..fc241a6655f 100644 --- a/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts +++ b/packages/twenty-front/src/modules/ui/layout/hooks/useShowAuthModal.ts @@ -1,19 +1,20 @@ import { useMemo } from 'react'; import { useRecoilValue } from 'recoil'; +import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus'; import { AppPath } from '@/types/AppPath'; import { isDefaultLayoutAuthModalVisibleState } from '@/ui/layout/states/isDefaultLayoutAuthModalVisibleState'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; -import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; +import { isDefined } from '~/utils/isDefined'; export const useShowAuthModal = () => { const isMatchingLocation = useIsMatchingLocation(); + const isLoggedIn = useIsLogged(); const onboardingStatus = useOnboardingStatus(); const subscriptionStatus = useSubscriptionStatus(); - const workspaceHasSubscription = useWorkspaceHasSubscription(); const isDefaultLayoutAuthModalVisible = useRecoilValue( isDefaultLayoutAuthModalVisibleState, ); @@ -28,7 +29,7 @@ export const useShowAuthModal = () => { return isDefaultLayoutAuthModalVisible; } if ( - !onboardingStatus || + !isLoggedIn || onboardingStatus === OnboardingStatus.PlanRequired || onboardingStatus === OnboardingStatus.ProfileCreation || onboardingStatus === OnboardingStatus.WorkspaceActivation || @@ -40,15 +41,15 @@ export const useShowAuthModal = () => { if (isMatchingLocation(AppPath.PlanRequired)) { return ( (onboardingStatus === OnboardingStatus.Completed && - !workspaceHasSubscription) || + !isDefined(subscriptionStatus)) || subscriptionStatus === SubscriptionStatus.Canceled ); } return false; }, [ + isLoggedIn, isDefaultLayoutAuthModalVisible, isMatchingLocation, - workspaceHasSubscription, onboardingStatus, subscriptionStatus, ]); diff --git a/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts b/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts index d0b8d4a9aab..332675639a0 100644 --- a/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts +++ b/packages/twenty-front/src/modules/workspace/hooks/useSubscriptionStatus.ts @@ -1,25 +1,9 @@ import { useRecoilValue } from 'recoil'; -import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { billingState } from '@/client-config/states/billingState'; import { SubscriptionStatus } from '~/generated/graphql'; -export const useSubscriptionStatus = (): - | SubscriptionStatus - | null - | undefined => { +export const useSubscriptionStatus = (): SubscriptionStatus | undefined => { const currentWorkspace = useRecoilValue(currentWorkspaceState); - const billing = useRecoilValue(billingState); - const isLoggedIn = useIsLogged(); - if (!isLoggedIn) { - return undefined; - } - if (!billing?.isBillingEnabled) { - return SubscriptionStatus.Active; - } - return ( - currentWorkspace?.currentBillingSubscription?.status || - SubscriptionStatus.Incomplete - ); + return currentWorkspace?.currentBillingSubscription?.status; }; diff --git a/packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts b/packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts deleted file mode 100644 index 7f0c4733083..00000000000 --- a/packages/twenty-front/src/modules/workspace/hooks/useWorkspaceHasSubscription.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useRecoilValue } from 'recoil'; - -import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; -import { isDefined } from '~/utils/isDefined'; - -export const useWorkspaceHasSubscription = () => { - const currentWorkspace = useRecoilValue(currentWorkspaceState); - - return isDefined(currentWorkspace?.currentBillingSubscription); -}; diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx index 73b6725907b..a91ff4e934d 100644 --- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx +++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx @@ -24,7 +24,6 @@ import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModa import { SubMenuTopBarContainer } from '@/ui/layout/page/SubMenuTopBarContainer'; import { Section } from '@/ui/layout/section/components/Section'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; -import { useWorkspaceHasSubscription } from '@/workspace/hooks/useWorkspaceHasSubscription'; import { OnboardingStatus, SubscriptionInterval, @@ -72,7 +71,6 @@ export const SettingsBilling = () => { const { enqueueSnackBar } = useSnackBar(); const onboardingStatus = useOnboardingStatus(); const subscriptionStatus = useSubscriptionStatus(); - const workspaceHasSubscription = useWorkspaceHasSubscription(); const currentWorkspace = useRecoilValue(currentWorkspaceState); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); const switchingInfo = @@ -108,7 +106,7 @@ export const SettingsBilling = () => { const displaySubscribeInfo = onboardingStatus === OnboardingStatus.Completed && - !workspaceHasSubscription; + !isDefined(subscriptionStatus); const openBillingPortal = () => { if (isDefined(data) && isDefined(data.billingPortalSession.url)) { diff --git a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts index 61fe5a14872..9832b41c6d6 100644 --- a/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts +++ b/packages/twenty-server/src/engine/core-modules/onboarding/onboarding.service.ts @@ -10,7 +10,6 @@ import { ConnectedAccountWorkspaceEntity } from 'src/modules/connected-account/s import { ConnectedAccountRepository } from 'src/modules/connected-account/repositories/connected-account.repository'; import { WorkspaceMemberWorkspaceEntity } from 'src/modules/workspace-member/standard-objects/workspace-member.workspace-entity'; import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service'; -import { EnvironmentService } from 'src/engine/integrations/environment/environment.service'; import { InjectWorkspaceRepository } from 'src/engine/twenty-orm/decorators/inject-workspace-repository.decorator'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { SubscriptionStatus } from 'src/engine/core-modules/billing/entities/billing-subscription.entity'; @@ -36,7 +35,6 @@ export class OnboardingService { constructor( private readonly billingService: BillingService, private readonly workspaceManagerService: WorkspaceManagerService, - private readonly environmentService: EnvironmentService, private readonly userWorkspaceService: UserWorkspaceService, private readonly keyValuePairService: KeyValuePairService, @InjectObjectMetadataRepository(ConnectedAccountWorkspaceEntity) From efccfcbe62030a9bf7a2a53843c6a044c68dde8c Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 28 Jun 2024 16:51:05 +0200 Subject: [PATCH 46/47] Fix tests --- ...sePageChangeEffectNavigateLocation.test.ts | 2 +- .../__tests__/useSubscriptionStatus.test.ts | 40 +------------------ 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts index 657529c191e..327e940ac3a 100644 --- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts +++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts @@ -290,7 +290,7 @@ describe('usePageChangeEffectNavigateLocation', () => { describe('tests should be exhaustive', () => { it('all location and onboarding status should be tested', () => { const untestedSubscriptionStatus = [ - SubscriptionStatus.Active, + SubscriptionStatus.Incomplete, SubscriptionStatus.IncompleteExpired, SubscriptionStatus.Paused, SubscriptionStatus.Trialing, diff --git a/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts b/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts index eaaa6a8218c..3ba84ddb77d 100644 --- a/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts +++ b/packages/twenty-front/src/modules/workspace/hooks/__tests__/useSubscriptionStatus.test.ts @@ -7,15 +7,9 @@ import { CurrentWorkspace, currentWorkspaceState, } from '@/auth/states/currentWorkspaceState'; -import { tokenPairState } from '@/auth/states/tokenPairState'; -import { billingState } from '@/client-config/states/billingState'; import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus'; import { SubscriptionStatus } from '~/generated/graphql'; -const tokenPair = { - accessToken: { token: 'accessToken', expiresAt: 'expiresAt' }, - refreshToken: { token: 'refreshToken', expiresAt: 'expiresAt' }, -}; const currentWorkspace = { id: '1', currentBillingSubscription: { status: SubscriptionStatus.Incomplete }, @@ -28,14 +22,10 @@ const renderHooks = () => { () => { const subscriptionStatus = useSubscriptionStatus(); const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState); - const setTokenPair = useSetRecoilState(tokenPairState); - const setBilling = useSetRecoilState(billingState); return { subscriptionStatus, setCurrentWorkspace, - setTokenPair, - setBilling, }; }, { @@ -46,38 +36,12 @@ const renderHooks = () => { }; describe('useSubscriptionStatus', () => { - it(`should return "undefined" when user is not logged in`, async () => { - const { result } = renderHooks(); - expect(result.current.subscriptionStatus).toBe(undefined); - }); - - Object.values(SubscriptionStatus).forEach((subscriptionStatus) => { - it(`should return "active" when billing not enabled`, async () => { - const { result } = renderHooks(); - const { setTokenPair, setCurrentWorkspace, setBilling } = result.current; - act(() => { - setBilling({ isBillingEnabled: false }); - setTokenPair(tokenPair); - setCurrentWorkspace({ - ...currentWorkspace, - currentBillingSubscription: { - id: v4(), - status: subscriptionStatus, - }, - }); - }); - expect(result.current.subscriptionStatus).toBe(SubscriptionStatus.Active); - }); - }); - Object.values(SubscriptionStatus).forEach((subscriptionStatus) => { - it(`should return "${subscriptionStatus}" when billing enabled`, async () => { + it(`should return "${subscriptionStatus}"`, async () => { const { result } = renderHooks(); - const { setTokenPair, setCurrentWorkspace, setBilling } = result.current; + const { setCurrentWorkspace } = result.current; act(() => { - setBilling({ isBillingEnabled: true }); - setTokenPair(tokenPair); setCurrentWorkspace({ ...currentWorkspace, currentBillingSubscription: { From d7a8eb5c47cdbc339bfa7d683c0b373244dd9c86 Mon Sep 17 00:00:00 2001 From: martmull Date: Fri, 28 Jun 2024 17:23:03 +0200 Subject: [PATCH 47/47] Use link instead of location replace --- .../ui/display/info/components/Info.tsx | 21 ++++++++++++++++++- .../src/pages/settings/SettingsBilling.tsx | 8 ++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx b/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx index 948dedda4e6..e97dbbb4388 100644 --- a/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx +++ b/packages/twenty-front/src/modules/ui/display/info/components/Info.tsx @@ -1,8 +1,10 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconInfoCircle } from 'twenty-ui'; +import { AppPath } from '@/types/AppPath'; import { Button } from '@/ui/input/button/components/Button'; export type InfoAccent = 'blue' | 'danger'; @@ -11,6 +13,7 @@ export type InfoProps = { text: string; buttonTitle?: string; onClick?: (event: React.MouseEvent) => void; + to?: AppPath; }; const StyledTextContainer = styled.div` @@ -47,11 +50,17 @@ const StyledInfo = styled.div>` } }} `; + +const StyledLink = styled(Link)` + text-decoration: none; +`; + export const Info = ({ accent = 'blue', text, buttonTitle, onClick, + to, }: InfoProps) => { const theme = useTheme(); return ( @@ -60,7 +69,17 @@ export const Info = ({ {text} - {buttonTitle && onClick && ( + {buttonTitle && to && ( + +