diff --git a/README.md b/README.md
index 9e7b96fe6..ff28b5986 100644
--- a/README.md
+++ b/README.md
@@ -26,12 +26,6 @@
# Demo
-
Go to demo.twenty.com and login with the following credentials:
```
diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts
index bf7297616..ba9400002 100644
--- a/packages/twenty-front/src/generated-metadata/graphql.ts
+++ b/packages/twenty-front/src/generated-metadata/graphql.ts
@@ -117,9 +117,9 @@ export type AvailableWorkspaceOutput = {
export type Billing = {
__typename?: 'Billing';
- billingFreeTrialDurationInDays?: Maybe;
billingUrl?: Maybe;
isBillingEnabled: Scalars['Boolean']['output'];
+ trialPeriods: Array;
};
/** The different billing plans available */
@@ -178,6 +178,7 @@ export type ClientConfig = {
api: ApiConfig;
authProviders: AuthProviders;
billing: Billing;
+ canManageFeatureFlags: Scalars['Boolean']['output'];
captcha: Captcha;
chromeExtensionId?: Maybe;
debugMode: Scalars['Boolean']['output'];
@@ -185,7 +186,6 @@ export type ClientConfig = {
frontDomain: Scalars['String']['output'];
isEmailVerificationRequired: Scalars['Boolean']['output'];
isMultiWorkspaceEnabled: Scalars['Boolean']['output'];
- isSSOEnabled: Scalars['Boolean']['output'];
sentry: Sentry;
signInPrefilled: Scalars['Boolean']['output'];
support: Support;
@@ -382,12 +382,6 @@ export type FeatureFlag = {
workspaceId: Scalars['String']['output'];
};
-export type FeatureFlagFilter = {
- and?: InputMaybe>;
- id?: InputMaybe;
- or?: InputMaybe>;
-};
-
export enum FeatureFlagKey {
IsAdvancedFiltersEnabled = 'IsAdvancedFiltersEnabled',
IsAirtableIntegrationEnabled = 'IsAirtableIntegrationEnabled',
@@ -402,22 +396,11 @@ export enum FeatureFlagKey {
IsJsonFilterEnabled = 'IsJsonFilterEnabled',
IsMicrosoftSyncEnabled = 'IsMicrosoftSyncEnabled',
IsPostgreSqlIntegrationEnabled = 'IsPostgreSQLIntegrationEnabled',
- IsSsoEnabled = 'IsSSOEnabled',
IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled',
IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled',
IsWorkflowEnabled = 'IsWorkflowEnabled'
}
-export type FeatureFlagSort = {
- direction: SortDirection;
- field: FeatureFlagSortFields;
- nulls?: InputMaybe;
-};
-
-export enum FeatureFlagSortFields {
- Id = 'id'
-}
-
export type FieldConnection = {
__typename?: 'FieldConnection';
/** Array of edges. */
@@ -859,6 +842,7 @@ export type MutationSignUpArgs = {
captchaToken?: InputMaybe;
email: Scalars['String']['input'];
password: Scalars['String']['input'];
+ workspaceId?: InputMaybe;
workspaceInviteHash?: InputMaybe;
workspacePersonalInviteToken?: InputMaybe;
};
@@ -1537,6 +1521,12 @@ export type TransientToken = {
transientToken: AuthToken;
};
+export type TrialPeriodDto = {
+ __typename?: 'TrialPeriodDTO';
+ duration: Scalars['Float']['output'];
+ isCreditCardRequired: Scalars['Boolean']['output'];
+};
+
export type UuidFilterComparison = {
eq?: InputMaybe;
gt?: InputMaybe;
@@ -1793,17 +1783,12 @@ export type WorkspaceBillingSubscriptionsArgs = {
sorting?: Array;
};
-
-export type WorkspaceFeatureFlagsArgs = {
- filter?: FeatureFlagFilter;
- sorting?: Array;
-};
-
export enum WorkspaceActivationStatus {
Active = 'ACTIVE',
Inactive = 'INACTIVE',
OngoingCreation = 'ONGOING_CREATION',
- PendingCreation = 'PENDING_CREATION'
+ PendingCreation = 'PENDING_CREATION',
+ Suspended = 'SUSPENDED'
}
export type WorkspaceEdge = {
diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx
index 40c09c186..6e738b9d1 100644
--- a/packages/twenty-front/src/generated/graphql.tsx
+++ b/packages/twenty-front/src/generated/graphql.tsx
@@ -1,5 +1,5 @@
-import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
+import { gql } from '@apollo/client';
export type Maybe = T | null;
export type InputMaybe = Maybe;
export type Exact = { [K in keyof T]: T[K] };
@@ -110,9 +110,9 @@ export type AvailableWorkspaceOutput = {
export type Billing = {
__typename?: 'Billing';
- billingFreeTrialDurationInDays?: Maybe;
billingUrl?: Maybe;
isBillingEnabled: Scalars['Boolean'];
+ trialPeriods: Array;
};
/** The different billing plans available */
@@ -1311,6 +1311,12 @@ export type TransientToken = {
transientToken: AuthToken;
};
+export type TrialPeriodDto = {
+ __typename?: 'TrialPeriodDTO';
+ duration: Scalars['Float'];
+ isCreditCardRequired: Scalars['Boolean'];
+};
+
export type UuidFilterComparison = {
eq?: InputMaybe;
gt?: InputMaybe;
@@ -2094,7 +2100,7 @@ export type UpdateBillingSubscriptionMutation = { __typename?: 'Mutation', updat
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
-export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, isEmailVerificationRequired: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, canManageFeatureFlags: boolean, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, billingFreeTrialDurationInDays?: number | null }, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
+export type GetClientConfigQuery = { __typename?: 'Query', clientConfig: { __typename?: 'ClientConfig', signInPrefilled: boolean, isMultiWorkspaceEnabled: boolean, defaultSubdomain?: string | null, frontDomain: string, debugMode: boolean, analyticsEnabled: boolean, chromeExtensionId?: string | null, canManageFeatureFlags: boolean, billing: { __typename?: 'Billing', isBillingEnabled: boolean, billingUrl?: string | null, trialPeriods: Array<{ __typename?: 'TrialPeriodDTO', duration: number, isCreditCardRequired: boolean }> }, authProviders: { __typename?: 'AuthProviders', google: boolean, password: boolean, microsoft: boolean, sso: Array<{ __typename?: 'SSOIdentityProvider', id: string, name: string, type: IdentityProviderType, status: SsoIdentityProviderStatus, issuer: string }> }, support: { __typename?: 'Support', supportDriver: string, supportFrontChatId?: string | null }, sentry: { __typename?: 'Sentry', dsn?: string | null, environment?: string | null, release?: string | null }, captcha: { __typename?: 'Captcha', provider?: CaptchaDriverType | null, siteKey?: string | null }, api: { __typename?: 'ApiConfig', mutationMaximumAffectedRecords: number } } };
export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>;
@@ -3564,7 +3570,10 @@ export const GetClientConfigDocument = gql`
billing {
isBillingEnabled
billingUrl
- billingFreeTrialDurationInDays
+ trialPeriods {
+ duration
+ isCreditCardRequired
+ }
}
authProviders {
google
diff --git a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts
index a7316f98a..8ad603c7b 100644
--- a/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts
+++ b/packages/twenty-front/src/hooks/__tests__/usePageChangeEffectNavigateLocation.test.ts
@@ -2,9 +2,9 @@ import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath';
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { AppPath } from '@/types/AppPath';
+import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
-import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
-import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
+import { OnboardingStatus } from '~/generated/graphql';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
@@ -17,11 +17,13 @@ const setupMockOnboardingStatus = (
jest.mocked(useOnboardingStatus).mockReturnValueOnce(onboardingStatus);
};
-jest.mock('@/workspace/hooks/useSubscriptionStatus');
-const setupMockSubscriptionStatus = (
- subscriptionStatus: SubscriptionStatus | undefined,
+jest.mock('@/workspace/hooks/useIsWorkspaceActivationStatusSuspended');
+const setupMockIsWorkspaceActivationStatusSuspended = (
+ isWorkspaceSuspended: boolean,
) => {
- jest.mocked(useSubscriptionStatus).mockReturnValueOnce(subscriptionStatus);
+ jest
+ .mocked(useIsWorkspaceActivationStatusSuspended)
+ .mockReturnValueOnce(isWorkspaceSuspended);
};
jest.mock('~/hooks/useIsMatchingLocation');
@@ -47,262 +49,206 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({
// prettier-ignore
const testCases = [
- { 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.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.Verify, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
+ { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
- { loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.Completed, res: undefined },
+ { loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.SignInUp, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
+ { loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
+ { loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: undefined },
+ { loc: AppPath.Invite, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
+ { loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
+ { loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
+ { loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
+ { loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
+ { loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
+ { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: undefined },
+ { loc: AppPath.ResetPassword, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
+ { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
+ { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
+ { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
+ { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
+ { loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.CreateWorkspace, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
+ { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.CreateProfile, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
+ { loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.SyncEmails, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
+ { loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.InviteTeam, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
+ { loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
+ { loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.PlanRequired, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
+ { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.PlanRequiredSuccess, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
+ { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
- { loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
- { 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.Index, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.TasksPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.OpportunitiesPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.RecordIndexPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.RecordShowPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: undefined },
+ { loc: AppPath.SettingsCatchAll, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.DevelopersCatchAll, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, 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.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.Authorize, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, 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, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.NotFoundWildcard, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, 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, 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 },
+ { loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
+ { loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
+ { loc: AppPath.NotFound, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
+ { loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
+ { loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
+ { loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
+ { loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
+ { loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
];
describe('usePageChangeEffectNavigateLocation', () => {
testCases.forEach((testCase) => {
- it(`with location ${testCase.loc} and onboardingStatus ${testCase.onboardingStatus} and subscriptionStatus ${testCase.subscriptionStatus} should return ${testCase.res}`, () => {
+ it(`with location ${testCase.loc} and onboardingStatus ${testCase.onboardingStatus} and isWorkspaceSuspended ${testCase.isWorkspaceSuspended} should return ${testCase.res}`, () => {
setupMockIsMatchingLocation(testCase.loc);
setupMockOnboardingStatus(testCase.onboardingStatus);
- setupMockSubscriptionStatus(testCase.subscriptionStatus);
+ setupMockIsWorkspaceActivationStatusSuspended(
+ testCase.isWorkspaceSuspended,
+ );
setupMockIsLogged(testCase.isLoggedIn);
expect(usePageChangeEffectNavigateLocation()).toEqual(testCase.res);
});
});
describe('tests should be exhaustive', () => {
- it('all location and onboarding status should be tested', () => {
- const untestedSubscriptionStatus = [
- SubscriptionStatus.Incomplete,
- SubscriptionStatus.IncompleteExpired,
- SubscriptionStatus.Paused,
- SubscriptionStatus.Trialing,
- ];
+ it('all location, onboarding status and suspended/not suspended workspace activation status should be tested', () => {
expect(testCases.length).toEqual(
(Object.keys(AppPath).length - UNTESTED_APP_PATHS.length) *
(Object.keys(OnboardingStatus).length +
- (Object.keys(SubscriptionStatus).length -
- untestedSubscriptionStatus.length)),
+ ['isWorkspaceSuspended:true', 'isWorkspaceSuspended:false'].length),
);
});
});
diff --git a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts
index ff2821aee..a5153cb75 100644
--- a/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts
+++ b/packages/twenty-front/src/hooks/usePageChangeEffectNavigateLocation.ts
@@ -3,15 +3,15 @@ import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePat
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath';
-import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
-import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
+import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
+import { OnboardingStatus } from '~/generated/graphql';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
export const usePageChangeEffectNavigateLocation = () => {
const isMatchingLocation = useIsMatchingLocation();
const isLoggedIn = useIsLogged();
const onboardingStatus = useOnboardingStatus();
- const subscriptionStatus = useSubscriptionStatus();
+ const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
const { defaultHomePagePath } = useDefaultHomePagePath();
const isMatchingOpenRoute =
@@ -49,22 +49,7 @@ export const usePageChangeEffectNavigateLocation = () => {
return AppPath.PlanRequired;
}
- if (
- subscriptionStatus === SubscriptionStatus.Unpaid &&
- !isMatchingLocation(AppPath.SettingsCatchAll)
- ) {
- return `${AppPath.SettingsCatchAll.replace('/*', '')}/${
- SettingsPath.Billing
- }`;
- }
-
- if (
- subscriptionStatus === SubscriptionStatus.Canceled &&
- !(
- isMatchingLocation(AppPath.SettingsCatchAll) ||
- isMatchingLocation(AppPath.PlanRequired)
- )
- ) {
+ if (isWorkspaceSuspended && !isMatchingLocation(AppPath.SettingsCatchAll)) {
return `${AppPath.SettingsCatchAll.replace('/*', '')}/${
SettingsPath.Billing
}`;
@@ -99,14 +84,6 @@ export const usePageChangeEffectNavigateLocation = () => {
return AppPath.InviteTeam;
}
- if (
- onboardingStatus === OnboardingStatus.Completed &&
- subscriptionStatus === SubscriptionStatus.Canceled &&
- isMatchingLocation(AppPath.PlanRequired)
- ) {
- return;
- }
-
if (
onboardingStatus === OnboardingStatus.Completed &&
isMatchingOnboardingRoute &&
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts
index 5a7072cea..659c2b7d2 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithGoogle.test.ts
@@ -19,7 +19,6 @@ describe('useSignInWithGoogle', () => {
plan: BillingPlanKey.Pro,
interval: SubscriptionInterval.Month,
requirePaymentMethod: true,
- skipPlanPage: false,
};
const Wrapper = getJestMetadataAndApolloMocksWrapper({
@@ -31,7 +30,7 @@ describe('useSignInWithGoogle', () => {
const mockUseParams = { workspaceInviteHash: 'testHash' };
const mockSearchParams = new URLSearchParams(
- 'inviteToken=testToken&billingCheckoutSessionState={"plan":"Pro","interval":"Month","requirePaymentMethod":true,"skipPlanPage":false}',
+ 'inviteToken=testToken&billingCheckoutSessionState={"plan":"Pro","interval":"Month","requirePaymentMethod":true}',
);
(useParams as jest.Mock).mockReturnValue(mockUseParams);
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts
index 98c9ea527..e4a5b1b2c 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/__tests__/useSignInWithMicrosoft.test.ts
@@ -22,7 +22,6 @@ describe('useSignInWithMicrosoft', () => {
plan: 'PRO',
interval: 'Month',
requirePaymentMethod: true,
- skipPlanPage: false,
};
it('should call signInWithMicrosoft with the correct parameters', () => {
diff --git a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts
index 26908402e..4d8361d03 100644
--- a/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts
+++ b/packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInWithGoogle.ts
@@ -12,7 +12,6 @@ export const useSignInWithGoogle = () => {
plan: 'PRO',
interval: 'Month',
requirePaymentMethod: true,
- skipPlanPage: false,
} as BillingCheckoutSession;
const { signInWithGoogle } = useAuth();
diff --git a/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
index acfe231a9..e80cd0e16 100644
--- a/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
+++ b/packages/twenty-front/src/modules/auth/states/billingCheckoutSessionState.ts
@@ -1,16 +1,11 @@
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
+import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
import { createState } from '@ui/utilities/state/utils/createState';
import { syncEffect } from 'recoil-sync';
-import { BillingPlanKey, SubscriptionInterval } from '~/generated/graphql';
export const billingCheckoutSessionState = createState({
key: 'billingCheckoutSessionState',
- defaultValue: {
- plan: BillingPlanKey.Pro,
- interval: SubscriptionInterval.Month,
- requirePaymentMethod: true,
- skipPlanPage: false,
- },
+ defaultValue: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
effects: [
syncEffect({
refine: (value: unknown) => {
@@ -19,8 +14,7 @@ export const billingCheckoutSessionState = createState({
value !== null &&
'plan' in value &&
'interval' in value &&
- 'requirePaymentMethod' in value &&
- 'skipPlanPage' in value
+ 'requirePaymentMethod' in value
) {
return {
type: 'success',
diff --git a/packages/twenty-front/src/modules/auth/types/billingCheckoutSession.type.ts b/packages/twenty-front/src/modules/auth/types/billingCheckoutSession.type.ts
index a634e17e2..8de24c98f 100644
--- a/packages/twenty-front/src/modules/auth/types/billingCheckoutSession.type.ts
+++ b/packages/twenty-front/src/modules/auth/types/billingCheckoutSession.type.ts
@@ -5,5 +5,4 @@ export type BillingCheckoutSession = {
plan: BillingPlanKey;
interval: SubscriptionInterval;
requirePaymentMethod: boolean;
- skipPlanPage: boolean;
};
diff --git a/packages/twenty-front/src/modules/billing/components/SubscriptionCard.tsx b/packages/twenty-front/src/modules/billing/components/SubscriptionCard.tsx
deleted file mode 100644
index 5bfbebee7..000000000
--- a/packages/twenty-front/src/modules/billing/components/SubscriptionCard.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import styled from '@emotion/styled';
-
-import { SubscriptionCardPrice } from '@/billing/components/SubscriptionCardPrice';
-import { capitalize } from 'twenty-shared';
-
-type SubscriptionCardProps = {
- type?: string;
- price: number;
- info: string;
-};
-
-const StyledSubscriptionCardContainer = styled.div`
- display: flex;
- flex-direction: column;
-`;
-
-const StyledTypeContainer = styled.div`
- color: ${({ theme }) => theme.font.color.secondary};
- font-size: ${({ theme }) => theme.font.size.sm};
- display: flex;
-`;
-
-const StyledInfoContainer = styled.div`
- color: ${({ theme }) => theme.font.color.tertiary};
- font-size: ${({ theme }) => theme.font.size.sm};
- display: flex;
-`;
-
-export const SubscriptionCard = ({
- type,
- price,
- info,
-}: SubscriptionCardProps) => {
- return (
-
- {capitalize(type || '')}
-
- {info}
-
- );
-};
diff --git a/packages/twenty-front/src/modules/billing/components/SubscriptionCardPrice.tsx b/packages/twenty-front/src/modules/billing/components/SubscriptionCardPrice.tsx
deleted file mode 100644
index 8091c72ab..000000000
--- a/packages/twenty-front/src/modules/billing/components/SubscriptionCardPrice.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import styled from '@emotion/styled';
-
-type SubscriptionCardPriceProps = {
- price: number;
-};
-const StyledSubscriptionCardPriceContainer = styled.div`
- align-items: baseline;
- display: flex;
- gap: ${({ theme }) => theme.betweenSiblingsGap};
- margin: ${({ theme }) => theme.spacing(1)} 0
- ${({ theme }) => theme.spacing(2)};
-`;
-const StyledPriceSpan = styled.span`
- color: ${({ theme }) => theme.font.color.primary};
- font-size: ${({ theme }) => theme.font.size.xl};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
-`;
-const StyledSeatSpan = styled.span`
- color: ${({ theme }) => theme.font.color.light};
- font-size: ${({ theme }) => theme.font.size.md};
- font-weight: ${({ theme }) => theme.font.weight.medium};
-`;
-export const SubscriptionCardPrice = ({
- price,
-}: SubscriptionCardPriceProps) => {
- return (
-
- ${price}
- /
- seat
-
- );
-};
diff --git a/packages/twenty-front/src/modules/billing/components/SubscriptionPrice.tsx b/packages/twenty-front/src/modules/billing/components/SubscriptionPrice.tsx
new file mode 100644
index 000000000..206c9b768
--- /dev/null
+++ b/packages/twenty-front/src/modules/billing/components/SubscriptionPrice.tsx
@@ -0,0 +1,29 @@
+import styled from '@emotion/styled';
+import { SubscriptionInterval } from '~/generated-metadata/graphql';
+
+type SubscriptionPriceProps = {
+ type: SubscriptionInterval;
+ price: number;
+};
+
+const StyledPriceSpan = styled.span`
+ color: ${({ theme }) => theme.font.color.primary};
+ font-size: ${({ theme }) => theme.font.size.xxl};
+ font-weight: ${({ theme }) => theme.font.weight.semiBold};
+ margin-bottom: ${({ theme }) => theme.spacing(1)};
+`;
+
+const StyledPriceUnitSpan = styled.span`
+ color: ${({ theme }) => theme.font.color.light};
+ font-size: ${({ theme }) => theme.font.size.md};
+ font-weight: ${({ theme }) => theme.font.weight.medium};
+`;
+
+export const SubscriptionPrice = ({ type, price }: SubscriptionPriceProps) => {
+ return (
+ <>
+ {`$${price}`}
+ {`seat / ${type.toLocaleLowerCase()}`}
+ >
+ );
+};
diff --git a/packages/twenty-front/src/modules/billing/components/TrialCard.tsx b/packages/twenty-front/src/modules/billing/components/TrialCard.tsx
new file mode 100644
index 000000000..7ce30c484
--- /dev/null
+++ b/packages/twenty-front/src/modules/billing/components/TrialCard.tsx
@@ -0,0 +1,33 @@
+import styled from '@emotion/styled';
+
+type TrialCardProps = {
+ duration: number;
+ withCreditCard: boolean;
+};
+
+const StyledTrialCardContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+`;
+
+const StyledTrialDurationContainer = styled.div`
+ color: ${({ theme }) => theme.font.color.secondary};
+ font-size: ${({ theme }) => theme.font.size.sm};
+ display: flex;
+ margin-bottom: ${({ theme }) => theme.spacing(4)};
+`;
+
+const StyledCreditCardRequirementContainer = styled.div`
+ color: ${({ theme }) => theme.font.color.tertiary};
+ font-size: ${({ theme }) => theme.font.size.sm};
+ display: flex;
+`;
+
+export const TrialCard = ({ duration, withCreditCard }: TrialCardProps) => {
+ return (
+
+ {`${duration} days trial`}
+ {`${withCreditCard ? 'With Credit Card' : 'Without Credit Card'}`}
+
+ );
+};
diff --git a/packages/twenty-front/src/modules/billing/constants/BillingCheckoutSessionDefaultValue.ts b/packages/twenty-front/src/modules/billing/constants/BillingCheckoutSessionDefaultValue.ts
new file mode 100644
index 000000000..5dd12a661
--- /dev/null
+++ b/packages/twenty-front/src/modules/billing/constants/BillingCheckoutSessionDefaultValue.ts
@@ -0,0 +1,11 @@
+import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
+import {
+ BillingPlanKey,
+ SubscriptionInterval,
+} from '~/generated-metadata/graphql';
+
+export const BILLING_CHECKOUT_SESSION_DEFAULT_VALUE: BillingCheckoutSession = {
+ plan: BillingPlanKey.Pro,
+ interval: SubscriptionInterval.Month,
+ requirePaymentMethod: true,
+};
diff --git a/packages/twenty-front/src/modules/billing/hooks/useHandleCheckoutSession.ts b/packages/twenty-front/src/modules/billing/hooks/useHandleCheckoutSession.ts
new file mode 100644
index 000000000..788cea521
--- /dev/null
+++ b/packages/twenty-front/src/modules/billing/hooks/useHandleCheckoutSession.ts
@@ -0,0 +1,50 @@
+import { AppPath } from '@/types/AppPath';
+import { SettingsPath } from '@/types/SettingsPath';
+import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
+import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
+import { useState } from 'react';
+import {
+ BillingPlanKey,
+ SubscriptionInterval,
+} from '~/generated-metadata/graphql';
+import { useCheckoutSessionMutation } from '~/generated/graphql';
+
+export const useHandleCheckoutSession = ({
+ recurringInterval,
+ plan,
+ requirePaymentMethod,
+}: {
+ recurringInterval: SubscriptionInterval;
+ plan: BillingPlanKey;
+ requirePaymentMethod: boolean;
+}) => {
+ const { enqueueSnackBar } = useSnackBar();
+
+ const [checkoutSession] = useCheckoutSessionMutation();
+
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ const handleCheckoutSession = async () => {
+ setIsSubmitting(true);
+ const { data } = await checkoutSession({
+ variables: {
+ recurringInterval,
+ successUrlPath: `${AppPath.Settings}/${SettingsPath.Billing}`,
+ plan,
+ requirePaymentMethod,
+ },
+ });
+ setIsSubmitting(false);
+ if (!data?.checkoutSession.url) {
+ enqueueSnackBar(
+ 'Checkout session error. Please retry or contact Twenty team',
+ {
+ variant: SnackBarVariant.Error,
+ },
+ );
+ return;
+ }
+ window.location.replace(data.checkoutSession.url);
+ };
+ return { isSubmitting, handleCheckoutSession };
+};
diff --git a/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts b/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts
index 577614bce..fa6a0a669 100644
--- a/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts
+++ b/packages/twenty-front/src/modules/client-config/graphql/queries/getClientConfig.ts
@@ -6,7 +6,10 @@ export const GET_CLIENT_CONFIG = gql`
billing {
isBillingEnabled
billingUrl
- billingFreeTrialDurationInDays
+ trialPeriods {
+ duration
+ isCreditCardRequired
+ }
}
authProviders {
google
diff --git a/packages/twenty-front/src/modules/information-banner/components/InformationBanner.tsx b/packages/twenty-front/src/modules/information-banner/components/InformationBanner.tsx
index 2c951b2e0..2cb79486a 100644
--- a/packages/twenty-front/src/modules/information-banner/components/InformationBanner.tsx
+++ b/packages/twenty-front/src/modules/information-banner/components/InformationBanner.tsx
@@ -17,12 +17,14 @@ export const InformationBanner = ({
buttonTitle,
buttonIcon,
buttonOnClick,
+ isButtonDisabled = false,
}: {
message: string;
variant?: BannerVariant;
buttonTitle?: string;
buttonIcon?: IconComponent;
buttonOnClick?: () => void;
+ isButtonDisabled?: boolean;
}) => {
return (
@@ -35,6 +37,7 @@ export const InformationBanner = ({
size="small"
inverted
onClick={buttonOnClick}
+ disabled={isButtonDisabled}
/>
)}
diff --git a/packages/twenty-front/src/modules/information-banner/components/InformationBannerWrapper.tsx b/packages/twenty-front/src/modules/information-banner/components/InformationBannerWrapper.tsx
index 0ebacf1a9..9ceb953e2 100644
--- a/packages/twenty-front/src/modules/information-banner/components/InformationBannerWrapper.tsx
+++ b/packages/twenty-front/src/modules/information-banner/components/InformationBannerWrapper.tsx
@@ -1,6 +1,13 @@
+import { InformationBannerBillingSubscriptionPaused } from '@/information-banner/components/billing/InformationBannerBillingSubscriptionPaused';
+import { InformationBannerFailPaymentInfo } from '@/information-banner/components/billing/InformationBannerFailPaymentInfo';
+import { InformationBannerNoBillingSubscription } from '@/information-banner/components/billing/InformationBannerNoBillingSubscription';
import { InformationBannerReconnectAccountEmailAliases } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases';
import { InformationBannerReconnectAccountInsufficientPermissions } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions';
+import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
+import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import styled from '@emotion/styled';
+import { isDefined } from 'twenty-ui';
+import { SubscriptionStatus } from '~/generated-metadata/graphql';
const StyledInformationBannerWrapper = styled.div`
height: 40px;
@@ -12,10 +19,30 @@ const StyledInformationBannerWrapper = styled.div`
`;
export const InformationBannerWrapper = () => {
+ const subscriptionStatus = useSubscriptionStatus();
+ const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
+
+ const displayBillingSubscriptionPausedBanner =
+ isWorkspaceSuspended && subscriptionStatus === SubscriptionStatus.Paused;
+
+ const displayBillingSubscriptionCanceledBanner =
+ isWorkspaceSuspended && !isDefined(subscriptionStatus);
+
+ const displayFailPaymentInfoBanner =
+ subscriptionStatus === SubscriptionStatus.PastDue ||
+ subscriptionStatus === SubscriptionStatus.Unpaid;
+
return (
+ {displayBillingSubscriptionPausedBanner && (
+
+ )}
+ {displayBillingSubscriptionCanceledBanner && (
+
+ )}
+ {displayFailPaymentInfoBanner && }
);
};
diff --git a/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerBillingSubscriptionPaused.tsx b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerBillingSubscriptionPaused.tsx
new file mode 100644
index 000000000..397f00f7e
--- /dev/null
+++ b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerBillingSubscriptionPaused.tsx
@@ -0,0 +1,29 @@
+import { InformationBanner } from '@/information-banner/components/InformationBanner';
+import { AppPath } from '@/types/AppPath';
+import { SettingsPath } from '@/types/SettingsPath';
+import { isDefined } from 'twenty-ui';
+import { useBillingPortalSessionQuery } from '~/generated/graphql';
+
+export const InformationBannerBillingSubscriptionPaused = () => {
+ const { data, loading } = useBillingPortalSessionQuery({
+ variables: {
+ returnUrlPath: `${AppPath.Settings}/${SettingsPath.Billing}`,
+ },
+ });
+
+ const openBillingPortal = () => {
+ if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
+ window.location.replace(data.billingPortalSession.url);
+ }
+ };
+
+ return (
+ openBillingPortal()}
+ isButtonDisabled={loading || !isDefined(data)}
+ />
+ );
+};
diff --git a/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerFailPaymentInfo.tsx b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerFailPaymentInfo.tsx
new file mode 100644
index 000000000..d17b2e5d2
--- /dev/null
+++ b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerFailPaymentInfo.tsx
@@ -0,0 +1,29 @@
+import { InformationBanner } from '@/information-banner/components/InformationBanner';
+import { AppPath } from '@/types/AppPath';
+import { SettingsPath } from '@/types/SettingsPath';
+import { isDefined } from 'twenty-ui';
+import { useBillingPortalSessionQuery } from '~/generated/graphql';
+
+export const InformationBannerFailPaymentInfo = () => {
+ const { data, loading } = useBillingPortalSessionQuery({
+ variables: {
+ returnUrlPath: `${AppPath.Settings}/${SettingsPath.Billing}`,
+ },
+ });
+
+ const openBillingPortal = () => {
+ if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
+ window.location.replace(data.billingPortalSession.url);
+ }
+ };
+
+ return (
+ openBillingPortal()}
+ isButtonDisabled={loading || !isDefined(data)}
+ />
+ );
+};
diff --git a/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerNoBillingSubscription.tsx b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerNoBillingSubscription.tsx
new file mode 100644
index 000000000..eefaa20a0
--- /dev/null
+++ b/packages/twenty-front/src/modules/information-banner/components/billing/InformationBannerNoBillingSubscription.tsx
@@ -0,0 +1,21 @@
+import { BILLING_CHECKOUT_SESSION_DEFAULT_VALUE } from '@/billing/constants/BillingCheckoutSessionDefaultValue';
+import { useHandleCheckoutSession } from '@/billing/hooks/useHandleCheckoutSession';
+import { InformationBanner } from '@/information-banner/components/InformationBanner';
+
+export const InformationBannerNoBillingSubscription = () => {
+ const { handleCheckoutSession, isSubmitting } = useHandleCheckoutSession({
+ recurringInterval: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE.interval,
+ plan: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE.plan,
+ requirePaymentMethod: true,
+ });
+
+ return (
+ handleCheckoutSession()}
+ isButtonDisabled={isSubmitting}
+ />
+ );
+};
diff --git a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx
index 2c9d4d581..1808b4c3d 100644
--- a/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx
+++ b/packages/twenty-front/src/modules/prefetch/components/PrefetchRunQueriesEffect.tsx
@@ -10,11 +10,14 @@ import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
import { View } from '@/views/types/View';
+import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
import { isDefined } from '~/utils/isDefined';
export const PrefetchRunQueriesEffect = () => {
const currentUser = useRecoilValue(currentUserState);
+ const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
+
const { upsertRecordsInCache: upsertViewsInCache } =
usePrefetchRunQuery({
prefetchKey: PrefetchKey.AllViews,
@@ -42,7 +45,7 @@ export const PrefetchRunQueriesEffect = () => {
const { result } = useCombinedFindManyRecords({
operationSignatures,
- skip: !currentUser,
+ skip: !currentUser || isWorkspaceSuspended,
});
useEffect(() => {
diff --git a/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts b/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts
index 7df0fa892..ceaab4bd6 100644
--- a/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts
+++ b/packages/twenty-front/src/modules/prefetch/hooks/useIsPrefetchLoading.ts
@@ -1,8 +1,10 @@
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
+import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
import { useRecoilValue } from 'recoil';
export const useIsPrefetchLoading = () => {
+ const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
const isFavoriteFoldersPrefetched = useRecoilValue(
prefetchIsLoadedFamilyState(PrefetchKey.AllFavoritesFolders),
);
@@ -15,8 +17,9 @@ export const useIsPrefetchLoading = () => {
);
return (
- !areViewsPrefetched ||
- !areFavoritesPrefetched ||
- !isFavoriteFoldersPrefetched
+ !isWorkspaceSuspended &&
+ (!areViewsPrefetched ||
+ !areFavoritesPrefetched ||
+ !isFavoriteFoldersPrefetched)
);
};
diff --git a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx
index ec0c5bcfe..5dc94d7b1 100644
--- a/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx
+++ b/packages/twenty-front/src/modules/ui/navigation/navigation-drawer/components/NavigationDrawerBackButton.tsx
@@ -6,6 +6,7 @@ import { IconX, UndecoratedLink } from 'twenty-ui';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
+import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
type NavigationDrawerBackButtonProps = {
title: string;
@@ -51,6 +52,11 @@ export const NavigationDrawerBackButton = ({
navigationDrawerExpandedMemorizedState,
);
+ const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
+ if (isWorkspaceSuspended) {
+ return ;
+ }
+
return (
{
+ const currentWorkspace = useRecoilValue(currentWorkspaceState);
+ return (
+ currentWorkspace?.activationStatus === WorkspaceActivationStatus.Suspended
+ );
+};
diff --git a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
index 1d9098864..61827f9b5 100644
--- a/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
+++ b/packages/twenty-front/src/pages/onboarding/ChooseYourPlan.tsx
@@ -3,50 +3,63 @@ import { Title } from '@/auth/components/Title';
import { useAuth } from '@/auth/hooks/useAuth';
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
import { SubscriptionBenefit } from '@/billing/components/SubscriptionBenefit';
-import { SubscriptionCard } from '@/billing/components/SubscriptionCard';
+import { SubscriptionPrice } from '@/billing/components/SubscriptionPrice';
+import { TrialCard } from '@/billing/components/TrialCard';
+import { useHandleCheckoutSession } from '@/billing/hooks/useHandleCheckoutSession';
import { billingState } from '@/client-config/states/billingState';
-import { AppPath } from '@/types/AppPath';
-import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
-import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import styled from '@emotion/styled';
-import { isNonEmptyString, isNumber } from '@sniptt/guards';
-import { useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import {
ActionLink,
CAL_LINK,
CardPicker,
+ isDefined,
Loader,
MainButton,
} from 'twenty-ui';
-import {
- ProductPriceEntity,
- SubscriptionInterval,
- useCheckoutSessionMutation,
- useGetProductPricesQuery,
-} from '~/generated/graphql';
-import { isDefined } from '~/utils/isDefined';
+import { SubscriptionInterval } from '~/generated-metadata/graphql';
+import { useGetProductPricesQuery } from '~/generated/graphql';
-const StyledChoosePlanContainer = styled.div`
- display: flex;
- flex-direction: row;
- width: 100%;
- margin: ${({ theme }) => theme.spacing(8)} 0
- ${({ theme }) => theme.spacing(2)};
- gap: ${({ theme }) => theme.spacing(2)};
-`;
-
-const StyledBenefitsContainer = styled.div`
+const StyledSubscriptionContainer = styled.div<{
+ withLongerMarginBottom: boolean;
+}>`
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.md};
+
+ display: flex;
+ flex-direction: column;
+ margin: ${({ theme }) => theme.spacing(8)} 0
+ ${({ theme, withLongerMarginBottom }) =>
+ theme.spacing(withLongerMarginBottom ? 8 : 2)};
+ width: 100%;
+`;
+
+const StyledSubscriptionPriceContainer = styled.div`
+ align-items: center;
+ border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
+ display: flex;
+ flex-direction: column;
+ margin: ${({ theme }) => theme.spacing(4)} ${({ theme }) => theme.spacing(3)}
+ 0 ${({ theme }) => theme.spacing(4)};
+ padding-bottom: ${({ theme }) => theme.spacing(3)};
+`;
+
+const StyledBenefitsContainer = styled.div`
box-sizing: border-box;
display: flex;
flex-direction: column;
width: 100%;
gap: 16px;
padding: ${({ theme }) => theme.spacing(4)} ${({ theme }) => theme.spacing(3)};
+`;
+
+const StyledChooseTrialContainer = styled.div`
+ display: flex;
+ flex-direction: row;
+ width: 100%;
margin-bottom: ${({ theme }) => theme.spacing(8)};
+ gap: ${({ theme }) => theme.spacing(2)};
`;
const StyledLinkGroup = styled.div`
@@ -71,58 +84,48 @@ const benefits = [
'Email integration',
'Custom objects',
'API & Webhooks',
- 'Frequent updates',
- 'And much more',
+ '1 000 workflow node executions',
];
-
export const ChooseYourPlan = () => {
const billing = useRecoilValue(billingState);
- const [isSubmitting, setIsSubmitting] = useState(false);
-
- const { enqueueSnackBar } = useSnackBar();
-
const { data: prices } = useGetProductPricesQuery({
variables: { product: 'base-plan' },
});
+ const price = prices?.getProductPrices?.productPrices.find(
+ (productPrice) =>
+ productPrice.recurringInterval === SubscriptionInterval.Month,
+ );
+
+ const hasWithoutCreditCardTrialPeriod = billing?.trialPeriods.some(
+ (trialPeriod) =>
+ !trialPeriod.isCreditCardRequired && trialPeriod.duration !== 0,
+ );
+ const withCreditCardTrialPeriod = billing?.trialPeriods.find(
+ (trialPeriod) => trialPeriod.isCreditCardRequired,
+ );
+
const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState(
billingCheckoutSessionState,
);
- const [checkoutSession] = useCheckoutSessionMutation();
+ const { handleCheckoutSession, isSubmitting } = useHandleCheckoutSession({
+ recurringInterval: billingCheckoutSession.interval,
+ plan: billingCheckoutSession.plan,
+ requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
+ });
- const handleCheckoutSession = async () => {
- setIsSubmitting(true);
- const { data } = await checkoutSession({
- variables: {
- recurringInterval: billingCheckoutSession.interval,
- successUrlPath: AppPath.PlanRequiredSuccess,
- plan: billingCheckoutSession.plan,
- requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
- },
- });
- setIsSubmitting(false);
- if (!data?.checkoutSession.url) {
- enqueueSnackBar(
- 'Checkout session error. Please retry or contact Twenty team',
- {
- variant: SnackBarVariant.Error,
- },
- );
- return;
- }
- window.location.replace(data.checkoutSession.url);
- };
-
- const handleIntervalChange = (type?: SubscriptionInterval) => {
+ const handleTrialPeriodChange = (withCreditCard: boolean) => {
return () => {
- if (isNonEmptyString(type) && billingCheckoutSession.interval !== type) {
+ if (
+ isDefined(price) &&
+ billingCheckoutSession.requirePaymentMethod !== withCreditCard
+ ) {
setBillingCheckoutSession({
plan: billingCheckoutSession.plan,
- interval: type,
- requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
- skipPlanPage: false,
+ interval: price.recurringInterval,
+ requirePaymentMethod: withCreditCard,
});
}
};
@@ -130,65 +133,58 @@ export const ChooseYourPlan = () => {
const { signOut } = useAuth();
- const computeInfo = (
- price: ProductPriceEntity,
- prices: ProductPriceEntity[],
- ): string => {
- if (price.recurringInterval !== SubscriptionInterval.Year) {
- return 'Cancel anytime';
- }
- const monthPrice = prices.filter(
- (price) => price.recurringInterval === SubscriptionInterval.Month,
- )?.[0];
- if (
- isDefined(monthPrice) &&
- isNumber(monthPrice.unitAmount) &&
- monthPrice.unitAmount > 0 &&
- isNumber(price.unitAmount) &&
- price.unitAmount > 0
- ) {
- return `Save $${(12 * monthPrice.unitAmount - price.unitAmount) / 100}`;
- }
- return 'Cancel anytime';
- };
-
- if (billingCheckoutSession.skipPlanPage && !isSubmitting) {
- handleCheckoutSession();
- }
-
- if (billingCheckoutSession.skipPlanPage && isSubmitting) {
- return ;
- }
-
return (
- prices?.getProductPrices?.productPrices && (
+ isDefined(price) &&
+ isDefined(billing) && (
<>
- Choose your Plan
-
- Enjoy a {billing?.billingFreeTrialDurationInDays}-day free trial
-
-
- {prices.getProductPrices.productPrices.map((price, index) => (
-
-
-
- ))}
-
-
- {benefits.map((benefit, index) => (
- {benefit}
- ))}
-
+
+ {hasWithoutCreditCardTrialPeriod
+ ? 'Choose your Trial'
+ : 'Get your subscription'}
+
+ {hasWithoutCreditCardTrialPeriod ? (
+ Cancel anytime
+ ) : (
+ withCreditCardTrialPeriod && (
+ {`Enjoy a ${withCreditCardTrialPeriod.duration}-days free trial`}
+ )
+ )}
+
+
+
+
+
+ {benefits.map((benefit) => (
+ {benefit}
+ ))}
+
+
+ {hasWithoutCreditCardTrialPeriod && (
+
+ {billing.trialPeriods.map((trialPeriod) => (
+
+
+
+ ))}
+
+ )}
= {
{
__typename: 'ProductPriceEntity',
created: 1699860608,
- recurringInterval: 'month',
+ recurringInterval: 'Month',
stripePriceId: 'monthly8usd',
unitAmount: 900,
},
{
__typename: 'ProductPriceEntity',
created: 1701874964,
- recurringInterval: 'year',
+ recurringInterval: 'Year',
stripePriceId: 'priceId',
unitAmount: 9000,
},
@@ -56,7 +56,7 @@ const meta: Meta = {
},
});
}),
- graphqlMocks.handlers,
+ ...graphqlMocks.handlers,
],
},
},
@@ -70,7 +70,7 @@ export const Default: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
- await canvas.findByText('Choose your Plan', undefined, {
+ await canvas.findByText('Choose your Trial', undefined, {
timeout: 3000,
});
},
diff --git a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx
index 495474723..b78280399 100644
--- a/packages/twenty-front/src/pages/settings/SettingsBilling.tsx
+++ b/packages/twenty-front/src/pages/settings/SettingsBilling.tsx
@@ -6,7 +6,6 @@ import {
IconCalendarEvent,
IconCircleX,
IconCreditCard,
- Info,
Section,
} from 'twenty-ui';
@@ -15,7 +14,6 @@ import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingC
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
-import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
@@ -25,7 +23,6 @@ import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import {
OnboardingStatus,
SubscriptionInterval,
- SubscriptionStatus,
useBillingPortalSessionQuery,
useUpdateBillingSubscriptionMutation,
} from '~/generated/graphql';
@@ -87,17 +84,6 @@ export const SettingsBilling = () => {
billingPortalButtonDisabled ||
onboardingStatus !== OnboardingStatus.Completed;
- const displayPaymentFailInfo =
- subscriptionStatus === SubscriptionStatus.PastDue ||
- subscriptionStatus === SubscriptionStatus.Unpaid;
-
- const displaySubscriptionCanceledInfo =
- subscriptionStatus === SubscriptionStatus.Canceled;
-
- const displaySubscribeInfo =
- onboardingStatus === OnboardingStatus.Completed &&
- !isDefined(subscriptionStatus);
-
const openBillingPortal = () => {
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
window.location.replace(data.billingPortalSession.url);
@@ -147,30 +133,7 @@ export const SettingsBilling = () => {
>
- {displayPaymentFailInfo && (
-
- )}
- {displaySubscriptionCanceledInfo && (
-
- )}
- {displaySubscribeInfo ? (
-
- ) : (
+ {isDefined(subscriptionStatus) && (
<>
Number)
+ @Min(0)
+ duration: number;
+
+ @Field(() => Boolean)
+ isCreditCardRequired: boolean;
+}
diff --git a/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts b/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts
index 896679364..89c661e05 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/services/billing-portal.workspace-service.ts
@@ -1,6 +1,7 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
+import { isDefined } from 'class-validator';
import { Repository } from 'typeorm';
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
@@ -50,15 +51,15 @@ export class BillingPortalWorkspaceService {
workspaceId: workspace.id,
});
- const stripeCustomerId = (
- await this.billingSubscriptionRepository.findOneBy({
- workspaceId: workspace.id,
- })
- )?.stripeCustomerId;
+ const subscription = await this.billingSubscriptionRepository.findOneBy({
+ workspaceId: workspace.id,
+ });
- const session = await this.stripeCheckoutService.createCheckoutSession(
+ const stripeCustomerId = subscription?.stripeCustomerId;
+
+ const session = await this.stripeCheckoutService.createCheckoutSession({
user,
- workspace.id,
+ workspaceId: workspace.id,
priceId,
quantity,
successUrl,
@@ -66,7 +67,8 @@ export class BillingPortalWorkspaceService {
stripeCustomerId,
plan,
requirePaymentMethod,
- );
+ withTrialPeriod: !isDefined(subscription),
+ });
assert(session.url, 'Error: missing checkout.session.url');
diff --git a/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts b/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts
index 647751987..6e30b9f3c 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/services/billing.service.ts
@@ -1,9 +1,11 @@
import { Injectable, Logger } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
import { isDefined } from 'class-validator';
+import { Repository } from 'typeorm';
+import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
import { BillingEntitlementKey } from 'src/engine/core-modules/billing/enums/billing-entitlement-key.enum';
-import { SubscriptionStatus } from 'src/engine/core-modules/billing/enums/billing-subscription-status.enum';
import { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.service';
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
@@ -16,15 +18,41 @@ export class BillingService {
private readonly environmentService: EnvironmentService,
private readonly billingSubscriptionService: BillingSubscriptionService,
private readonly isFeatureEnabledService: FeatureFlagService,
+ @InjectRepository(BillingSubscription, 'core')
+ private readonly billingSubscriptionRepository: Repository,
) {}
isBillingEnabled() {
return this.environmentService.get('IS_BILLING_ENABLED');
}
- async hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement(
+ async hasWorkspaceSubscriptionOrFreeAccess(workspaceId: string) {
+ const isBillingEnabled = this.isBillingEnabled();
+
+ if (!isBillingEnabled) {
+ return true;
+ }
+
+ const isFreeAccessEnabled =
+ await this.isFeatureEnabledService.isFeatureEnabled(
+ FeatureFlagKey.IsFreeAccessEnabled,
+ workspaceId,
+ );
+
+ if (isFreeAccessEnabled) {
+ return true;
+ }
+
+ const subscription = await this.billingSubscriptionRepository.findOne({
+ where: { workspaceId },
+ });
+
+ return isDefined(subscription);
+ }
+
+ async hasFreeAccessOrEntitlement(
workspaceId: string,
- entitlementKey?: BillingEntitlementKey,
+ entitlementKey: BillingEntitlementKey,
) {
const isBillingEnabled = this.isBillingEnabled();
@@ -42,25 +70,9 @@ export class BillingService {
return true;
}
- if (entitlementKey) {
- return this.billingSubscriptionService.getWorkspaceEntitlementByKey(
- workspaceId,
- entitlementKey,
- );
- }
-
- const currentBillingSubscription =
- await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow(
- { workspaceId },
- );
-
- return (
- isDefined(currentBillingSubscription) &&
- [
- SubscriptionStatus.Active,
- SubscriptionStatus.Trialing,
- SubscriptionStatus.PastDue,
- ].includes(currentBillingSubscription.status)
+ return this.billingSubscriptionService.getWorkspaceEntitlementByKey(
+ workspaceId,
+ entitlementKey,
);
}
}
diff --git a/packages/twenty-server/src/engine/core-modules/billing/stripe/services/stripe-checkout.service.ts b/packages/twenty-server/src/engine/core-modules/billing/stripe/services/stripe-checkout.service.ts
index fd7715275..bab412a8d 100644
--- a/packages/twenty-server/src/engine/core-modules/billing/stripe/services/stripe-checkout.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/billing/stripe/services/stripe-checkout.service.ts
@@ -24,17 +24,29 @@ export class StripeCheckoutService {
);
}
- async createCheckoutSession(
- user: User,
- workspaceId: string,
- priceId: string,
- quantity: number,
- successUrl?: string,
- cancelUrl?: string,
- stripeCustomerId?: string,
- plan: BillingPlanKey = BillingPlanKey.PRO,
+ async createCheckoutSession({
+ user,
+ workspaceId,
+ priceId,
+ quantity,
+ successUrl,
+ cancelUrl,
+ stripeCustomerId,
+ plan = BillingPlanKey.PRO,
requirePaymentMethod = true,
- ): Promise {
+ withTrialPeriod,
+ }: {
+ user: User;
+ workspaceId: string;
+ priceId: string;
+ quantity: number;
+ successUrl?: string;
+ cancelUrl?: string;
+ stripeCustomerId?: string;
+ plan?: BillingPlanKey;
+ requirePaymentMethod?: boolean;
+ withTrialPeriod: boolean;
+ }): Promise {
return await this.stripe.checkout.sessions.create({
line_items: [
{
@@ -48,14 +60,25 @@ export class StripeCheckoutService {
workspaceId,
plan,
},
- trial_period_days: this.environmentService.get(
- 'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
- ),
+ ...(withTrialPeriod
+ ? {
+ trial_period_days: this.environmentService.get(
+ requirePaymentMethod
+ ? 'BILLING_FREE_TRIAL_WITH_CREDIT_CARD_DURATION_IN_DAYS'
+ : 'BILLING_FREE_TRIAL_WITHOUT_CREDIT_CARD_DURATION_IN_DAYS',
+ ),
+ trial_settings: {
+ end_behavior: { missing_payment_method: 'pause' },
+ },
+ }
+ : {}),
},
automatic_tax: { enabled: !!requirePaymentMethod },
tax_id_collection: { enabled: !!requirePaymentMethod },
customer: stripeCustomerId,
- customer_update: stripeCustomerId ? { name: 'auto' } : undefined,
+ customer_update: stripeCustomerId
+ ? { name: 'auto', address: 'auto' }
+ : undefined,
customer_email: stripeCustomerId ? undefined : user.email,
success_url: successUrl,
cancel_url: cancelUrl,
diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts
index fcdab3c0c..69527b001 100644
--- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts
+++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.entity.ts
@@ -1,5 +1,6 @@
import { Field, ObjectType } from '@nestjs/graphql';
+import { TrialPeriodDTO } from 'src/engine/core-modules/billing/dto/trial-period.dto';
import { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output';
@@ -11,8 +12,8 @@ class Billing {
@Field(() => String, { nullable: true })
billingUrl?: string;
- @Field(() => Number, { nullable: true })
- billingFreeTrialDurationInDays?: number;
+ @Field(() => [TrialPeriodDTO])
+ trialPeriods: TrialPeriodDTO[];
}
@ObjectType()
diff --git a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts
index 9f6282f88..f932b3d6a 100644
--- a/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts
+++ b/packages/twenty-server/src/engine/core-modules/client-config/client-config.resolver.ts
@@ -18,9 +18,20 @@ export class ClientConfigResolver {
billing: {
isBillingEnabled: this.environmentService.get('IS_BILLING_ENABLED'),
billingUrl: this.environmentService.get('BILLING_PLAN_REQUIRED_LINK'),
- billingFreeTrialDurationInDays: this.environmentService.get(
- 'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
- ),
+ trialPeriods: [
+ {
+ duration: this.environmentService.get(
+ 'BILLING_FREE_TRIAL_WITH_CREDIT_CARD_DURATION_IN_DAYS',
+ ),
+ isCreditCardRequired: true,
+ },
+ {
+ duration: this.environmentService.get(
+ 'BILLING_FREE_TRIAL_WITHOUT_CREDIT_CARD_DURATION_IN_DAYS',
+ ),
+ isCreditCardRequired: false,
+ },
+ ],
},
authProviders: {
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
diff --git a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts
index 318321994..d7e67b996 100644
--- a/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts
+++ b/packages/twenty-server/src/engine/core-modules/environment/environment-variables.ts
@@ -73,7 +73,13 @@ export class EnvironmentVariables {
@CastToPositiveNumber()
@IsOptional()
@ValidateIf((env) => env.IS_BILLING_ENABLED === true)
- BILLING_FREE_TRIAL_DURATION_IN_DAYS = 7;
+ BILLING_FREE_TRIAL_WITH_CREDIT_CARD_DURATION_IN_DAYS = 30;
+
+ @IsNumber()
+ @CastToPositiveNumber()
+ @IsOptional()
+ @ValidateIf((env) => env.IS_BILLING_ENABLED === true)
+ BILLING_FREE_TRIAL_WITHOUT_CREDIT_CARD_DURATION_IN_DAYS = 7;
@IsString()
@ValidateIf((env) => env.IS_BILLING_ENABLED === true)
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 894369f71..053f011ae 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
@@ -30,7 +30,7 @@ export class OnboardingService {
private async isSubscriptionIncompleteOnboardingStatus(workspace: Workspace) {
const hasSubscription =
- await this.billingService.hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement(
+ await this.billingService.hasWorkspaceSubscriptionOrFreeAccess(
workspace.id,
);
diff --git a/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts b/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts
index 39e406cc8..fcfb35cb3 100644
--- a/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/sso/services/sso.service.ts
@@ -36,7 +36,7 @@ export class SSOService {
private async isSSOEnabled(workspaceId: string) {
const isSSOBillingEnabled =
- await this.billingService.hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement(
+ await this.billingService.hasFreeAccessOrEntitlement(
workspaceId,
this.featureLookUpKey,
);
diff --git a/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts b/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts
index 7eab38aea..231ce6a2a 100644
--- a/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts
+++ b/packages/twenty-server/src/engine/core-modules/user/services/user.service.ts
@@ -41,7 +41,10 @@ export class UserService extends TypeOrmQueryService {
}
async loadWorkspaceMember(user: User, workspace: Workspace) {
- if (workspace?.activationStatus !== WorkspaceActivationStatus.ACTIVE) {
+ if (
+ workspace?.activationStatus !== WorkspaceActivationStatus.ACTIVE &&
+ workspace?.activationStatus !== WorkspaceActivationStatus.SUSPENDED
+ ) {
return null;
}