Update ChooseYourPlan page with new trial period options (#9628)
### Context - Update /plan-required page to let users get free trial without credit card plan - Update usePageChangeEffectNavigateLocation to redirect paused and canceled subscription (suspended workspace) to /settings/billing page ### To do - [x] Update usePageChangeEffectNavigateLocation test - [x] Update ChooseYourPlan sb test closes #9520 --------- Co-authored-by: etiennejouan <jouan.etienne@gmail.com>
This commit is contained in:
@ -26,12 +26,6 @@
|
|||||||
|
|
||||||
# Demo
|
# Demo
|
||||||
|
|
||||||
<!--
|
|
||||||
You can use the following url to sign up to the cloud version without providing a credit card:
|
|
||||||
|
|
||||||
<a href="https://demo.twenty.com/?billingCheckoutSession={"plan":"PRO","recurringInterval":"MONTHLY","requirePaymentMethod":false,"skipPlanPage":true}">https://demo.twenty.com/?billingCheckoutSession={"plan":"PRO","recurringInterval":"MONTHLY","requirePaymentMethod":false,"skipPlanPage":true}</a>
|
|
||||||
|
|
||||||
-->
|
|
||||||
Go to <a href="https://demo.twenty.com/">demo.twenty.com</a> and login with the following credentials:
|
Go to <a href="https://demo.twenty.com/">demo.twenty.com</a> and login with the following credentials:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@ -117,9 +117,9 @@ export type AvailableWorkspaceOutput = {
|
|||||||
|
|
||||||
export type Billing = {
|
export type Billing = {
|
||||||
__typename?: 'Billing';
|
__typename?: 'Billing';
|
||||||
billingFreeTrialDurationInDays?: Maybe<Scalars['Float']['output']>;
|
|
||||||
billingUrl?: Maybe<Scalars['String']['output']>;
|
billingUrl?: Maybe<Scalars['String']['output']>;
|
||||||
isBillingEnabled: Scalars['Boolean']['output'];
|
isBillingEnabled: Scalars['Boolean']['output'];
|
||||||
|
trialPeriods: Array<TrialPeriodDto>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The different billing plans available */
|
/** The different billing plans available */
|
||||||
@ -178,6 +178,7 @@ export type ClientConfig = {
|
|||||||
api: ApiConfig;
|
api: ApiConfig;
|
||||||
authProviders: AuthProviders;
|
authProviders: AuthProviders;
|
||||||
billing: Billing;
|
billing: Billing;
|
||||||
|
canManageFeatureFlags: Scalars['Boolean']['output'];
|
||||||
captcha: Captcha;
|
captcha: Captcha;
|
||||||
chromeExtensionId?: Maybe<Scalars['String']['output']>;
|
chromeExtensionId?: Maybe<Scalars['String']['output']>;
|
||||||
debugMode: Scalars['Boolean']['output'];
|
debugMode: Scalars['Boolean']['output'];
|
||||||
@ -185,7 +186,6 @@ export type ClientConfig = {
|
|||||||
frontDomain: Scalars['String']['output'];
|
frontDomain: Scalars['String']['output'];
|
||||||
isEmailVerificationRequired: Scalars['Boolean']['output'];
|
isEmailVerificationRequired: Scalars['Boolean']['output'];
|
||||||
isMultiWorkspaceEnabled: Scalars['Boolean']['output'];
|
isMultiWorkspaceEnabled: Scalars['Boolean']['output'];
|
||||||
isSSOEnabled: Scalars['Boolean']['output'];
|
|
||||||
sentry: Sentry;
|
sentry: Sentry;
|
||||||
signInPrefilled: Scalars['Boolean']['output'];
|
signInPrefilled: Scalars['Boolean']['output'];
|
||||||
support: Support;
|
support: Support;
|
||||||
@ -382,12 +382,6 @@ export type FeatureFlag = {
|
|||||||
workspaceId: Scalars['String']['output'];
|
workspaceId: Scalars['String']['output'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FeatureFlagFilter = {
|
|
||||||
and?: InputMaybe<Array<FeatureFlagFilter>>;
|
|
||||||
id?: InputMaybe<UuidFilterComparison>;
|
|
||||||
or?: InputMaybe<Array<FeatureFlagFilter>>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum FeatureFlagKey {
|
export enum FeatureFlagKey {
|
||||||
IsAdvancedFiltersEnabled = 'IsAdvancedFiltersEnabled',
|
IsAdvancedFiltersEnabled = 'IsAdvancedFiltersEnabled',
|
||||||
IsAirtableIntegrationEnabled = 'IsAirtableIntegrationEnabled',
|
IsAirtableIntegrationEnabled = 'IsAirtableIntegrationEnabled',
|
||||||
@ -402,22 +396,11 @@ export enum FeatureFlagKey {
|
|||||||
IsJsonFilterEnabled = 'IsJsonFilterEnabled',
|
IsJsonFilterEnabled = 'IsJsonFilterEnabled',
|
||||||
IsMicrosoftSyncEnabled = 'IsMicrosoftSyncEnabled',
|
IsMicrosoftSyncEnabled = 'IsMicrosoftSyncEnabled',
|
||||||
IsPostgreSqlIntegrationEnabled = 'IsPostgreSQLIntegrationEnabled',
|
IsPostgreSqlIntegrationEnabled = 'IsPostgreSQLIntegrationEnabled',
|
||||||
IsSsoEnabled = 'IsSSOEnabled',
|
|
||||||
IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled',
|
IsStripeIntegrationEnabled = 'IsStripeIntegrationEnabled',
|
||||||
IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled',
|
IsUniqueIndexesEnabled = 'IsUniqueIndexesEnabled',
|
||||||
IsWorkflowEnabled = 'IsWorkflowEnabled'
|
IsWorkflowEnabled = 'IsWorkflowEnabled'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FeatureFlagSort = {
|
|
||||||
direction: SortDirection;
|
|
||||||
field: FeatureFlagSortFields;
|
|
||||||
nulls?: InputMaybe<SortNulls>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum FeatureFlagSortFields {
|
|
||||||
Id = 'id'
|
|
||||||
}
|
|
||||||
|
|
||||||
export type FieldConnection = {
|
export type FieldConnection = {
|
||||||
__typename?: 'FieldConnection';
|
__typename?: 'FieldConnection';
|
||||||
/** Array of edges. */
|
/** Array of edges. */
|
||||||
@ -859,6 +842,7 @@ export type MutationSignUpArgs = {
|
|||||||
captchaToken?: InputMaybe<Scalars['String']['input']>;
|
captchaToken?: InputMaybe<Scalars['String']['input']>;
|
||||||
email: Scalars['String']['input'];
|
email: Scalars['String']['input'];
|
||||||
password: Scalars['String']['input'];
|
password: Scalars['String']['input'];
|
||||||
|
workspaceId?: InputMaybe<Scalars['String']['input']>;
|
||||||
workspaceInviteHash?: InputMaybe<Scalars['String']['input']>;
|
workspaceInviteHash?: InputMaybe<Scalars['String']['input']>;
|
||||||
workspacePersonalInviteToken?: InputMaybe<Scalars['String']['input']>;
|
workspacePersonalInviteToken?: InputMaybe<Scalars['String']['input']>;
|
||||||
};
|
};
|
||||||
@ -1537,6 +1521,12 @@ export type TransientToken = {
|
|||||||
transientToken: AuthToken;
|
transientToken: AuthToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TrialPeriodDto = {
|
||||||
|
__typename?: 'TrialPeriodDTO';
|
||||||
|
duration: Scalars['Float']['output'];
|
||||||
|
isCreditCardRequired: Scalars['Boolean']['output'];
|
||||||
|
};
|
||||||
|
|
||||||
export type UuidFilterComparison = {
|
export type UuidFilterComparison = {
|
||||||
eq?: InputMaybe<Scalars['UUID']['input']>;
|
eq?: InputMaybe<Scalars['UUID']['input']>;
|
||||||
gt?: InputMaybe<Scalars['UUID']['input']>;
|
gt?: InputMaybe<Scalars['UUID']['input']>;
|
||||||
@ -1793,17 +1783,12 @@ export type WorkspaceBillingSubscriptionsArgs = {
|
|||||||
sorting?: Array<BillingSubscriptionSort>;
|
sorting?: Array<BillingSubscriptionSort>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export type WorkspaceFeatureFlagsArgs = {
|
|
||||||
filter?: FeatureFlagFilter;
|
|
||||||
sorting?: Array<FeatureFlagSort>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export enum WorkspaceActivationStatus {
|
export enum WorkspaceActivationStatus {
|
||||||
Active = 'ACTIVE',
|
Active = 'ACTIVE',
|
||||||
Inactive = 'INACTIVE',
|
Inactive = 'INACTIVE',
|
||||||
OngoingCreation = 'ONGOING_CREATION',
|
OngoingCreation = 'ONGOING_CREATION',
|
||||||
PendingCreation = 'PENDING_CREATION'
|
PendingCreation = 'PENDING_CREATION',
|
||||||
|
Suspended = 'SUSPENDED'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type WorkspaceEdge = {
|
export type WorkspaceEdge = {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
import * as Apollo from '@apollo/client';
|
import * as Apollo from '@apollo/client';
|
||||||
|
import { gql } from '@apollo/client';
|
||||||
export type Maybe<T> = T | null;
|
export type Maybe<T> = T | null;
|
||||||
export type InputMaybe<T> = Maybe<T>;
|
export type InputMaybe<T> = Maybe<T>;
|
||||||
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
||||||
@ -110,9 +110,9 @@ export type AvailableWorkspaceOutput = {
|
|||||||
|
|
||||||
export type Billing = {
|
export type Billing = {
|
||||||
__typename?: 'Billing';
|
__typename?: 'Billing';
|
||||||
billingFreeTrialDurationInDays?: Maybe<Scalars['Float']>;
|
|
||||||
billingUrl?: Maybe<Scalars['String']>;
|
billingUrl?: Maybe<Scalars['String']>;
|
||||||
isBillingEnabled: Scalars['Boolean'];
|
isBillingEnabled: Scalars['Boolean'];
|
||||||
|
trialPeriods: Array<TrialPeriodDto>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The different billing plans available */
|
/** The different billing plans available */
|
||||||
@ -1311,6 +1311,12 @@ export type TransientToken = {
|
|||||||
transientToken: AuthToken;
|
transientToken: AuthToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TrialPeriodDto = {
|
||||||
|
__typename?: 'TrialPeriodDTO';
|
||||||
|
duration: Scalars['Float'];
|
||||||
|
isCreditCardRequired: Scalars['Boolean'];
|
||||||
|
};
|
||||||
|
|
||||||
export type UuidFilterComparison = {
|
export type UuidFilterComparison = {
|
||||||
eq?: InputMaybe<Scalars['UUID']>;
|
eq?: InputMaybe<Scalars['UUID']>;
|
||||||
gt?: InputMaybe<Scalars['UUID']>;
|
gt?: InputMaybe<Scalars['UUID']>;
|
||||||
@ -2094,7 +2100,7 @@ export type UpdateBillingSubscriptionMutation = { __typename?: 'Mutation', updat
|
|||||||
export type GetClientConfigQueryVariables = Exact<{ [key: string]: never; }>;
|
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; }>;
|
export type SkipSyncEmailOnboardingStepMutationVariables = Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
@ -3564,7 +3570,10 @@ export const GetClientConfigDocument = gql`
|
|||||||
billing {
|
billing {
|
||||||
isBillingEnabled
|
isBillingEnabled
|
||||||
billingUrl
|
billingUrl
|
||||||
billingFreeTrialDurationInDays
|
trialPeriods {
|
||||||
|
duration
|
||||||
|
isCreditCardRequired
|
||||||
|
}
|
||||||
}
|
}
|
||||||
authProviders {
|
authProviders {
|
||||||
google
|
google
|
||||||
|
|||||||
@ -2,9 +2,9 @@ import { useIsLogged } from '@/auth/hooks/useIsLogged';
|
|||||||
import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath';
|
import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePath';
|
||||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
|
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
|
||||||
|
|
||||||
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
import { OnboardingStatus } from '~/generated/graphql';
|
||||||
import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
|
|
||||||
|
|
||||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||||
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
|
import { usePageChangeEffectNavigateLocation } from '~/hooks/usePageChangeEffectNavigateLocation';
|
||||||
@ -17,11 +17,13 @@ const setupMockOnboardingStatus = (
|
|||||||
jest.mocked(useOnboardingStatus).mockReturnValueOnce(onboardingStatus);
|
jest.mocked(useOnboardingStatus).mockReturnValueOnce(onboardingStatus);
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock('@/workspace/hooks/useSubscriptionStatus');
|
jest.mock('@/workspace/hooks/useIsWorkspaceActivationStatusSuspended');
|
||||||
const setupMockSubscriptionStatus = (
|
const setupMockIsWorkspaceActivationStatusSuspended = (
|
||||||
subscriptionStatus: SubscriptionStatus | undefined,
|
isWorkspaceSuspended: boolean,
|
||||||
) => {
|
) => {
|
||||||
jest.mocked(useSubscriptionStatus).mockReturnValueOnce(subscriptionStatus);
|
jest
|
||||||
|
.mocked(useIsWorkspaceActivationStatusSuspended)
|
||||||
|
.mockReturnValueOnce(isWorkspaceSuspended);
|
||||||
};
|
};
|
||||||
|
|
||||||
jest.mock('~/hooks/useIsMatchingLocation');
|
jest.mock('~/hooks/useIsMatchingLocation');
|
||||||
@ -47,262 +49,206 @@ jest.mocked(useDefaultHomePagePath).mockReturnValue({
|
|||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const testCases = [
|
const testCases = [
|
||||||
{ loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, 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, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.Verify, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
||||||
{ loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.Verify, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined },
|
{ loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.Verify, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.Verify, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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.VerifyEmail, isLoggedIn: true, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.VerifyEmail, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
|
{ loc: AppPath.SignInUp, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.Invite, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
|
||||||
{ loc: AppPath.SignInUp, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.Invite, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
|
||||||
{ loc: AppPath.Invite, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
|
{ loc: AppPath.ResetPassword, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.ResetPassword, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
|
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.CreateProfile, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.CreateWorkspace, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
|
||||||
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
{ loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.CreateWorkspace, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.CreateProfile, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.SyncEmails, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.CreateProfile, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
|
||||||
{ loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: undefined },
|
{ loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.CreateProfile, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.SyncEmails, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.InviteTeam, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.SyncEmails, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: undefined },
|
||||||
{ loc: AppPath.SyncEmails, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: undefined },
|
{ loc: AppPath.InviteTeam, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
||||||
{ loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.PlanRequired, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.InviteTeam, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.InviteTeam, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.PlanRequired, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
||||||
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
||||||
{ loc: AppPath.PlanRequired, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.PlanRequired, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: undefined },
|
{ loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.Index, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: undefined },
|
{ loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.PlanRequiredSuccess, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.Index, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.TasksPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: defaultHomePagePath },
|
{ loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.Index, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.Index, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.TasksPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.TasksPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.TasksPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.OpportunitiesPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.OpportunitiesPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.RecordIndexPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.RecordIndexPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.RecordShowPage, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.RecordShowPage, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.RecordShowPage, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.SettingsCatchAll, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.SettingsCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.Authorize, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.DevelopersCatchAll, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.Authorize, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.Authorize, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.Authorize, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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, subscriptionStatus: undefined, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
{ loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.PlanRequired, res: AppPath.PlanRequired },
|
||||||
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Canceled, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: true, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
||||||
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Unpaid, onboardingStatus: OnboardingStatus.Completed, res: '/settings/billing' },
|
{ loc: AppPath.NotFound, isLoggedIn: false, isWorkspaceSuspended: false, onboardingStatus: undefined, res: AppPath.SignInUp },
|
||||||
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.PastDue, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
{ loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
||||||
{ loc: AppPath.NotFoundWildcard, isLoggedIn: false, subscriptionStatus: undefined, onboardingStatus: undefined, res: AppPath.SignInUp },
|
{ loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
||||||
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.WorkspaceActivation, res: AppPath.CreateWorkspace },
|
{ loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
||||||
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.ProfileCreation, res: AppPath.CreateProfile },
|
{ loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.InviteTeam, res: AppPath.InviteTeam },
|
||||||
{ loc: AppPath.NotFoundWildcard, isLoggedIn: true, subscriptionStatus: SubscriptionStatus.Active, onboardingStatus: OnboardingStatus.SyncEmail, res: AppPath.SyncEmails },
|
{ loc: AppPath.NotFound, isLoggedIn: true, isWorkspaceSuspended: false, onboardingStatus: OnboardingStatus.Completed, res: undefined },
|
||||||
{ 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 },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
describe('usePageChangeEffectNavigateLocation', () => {
|
describe('usePageChangeEffectNavigateLocation', () => {
|
||||||
testCases.forEach((testCase) => {
|
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);
|
setupMockIsMatchingLocation(testCase.loc);
|
||||||
setupMockOnboardingStatus(testCase.onboardingStatus);
|
setupMockOnboardingStatus(testCase.onboardingStatus);
|
||||||
setupMockSubscriptionStatus(testCase.subscriptionStatus);
|
setupMockIsWorkspaceActivationStatusSuspended(
|
||||||
|
testCase.isWorkspaceSuspended,
|
||||||
|
);
|
||||||
setupMockIsLogged(testCase.isLoggedIn);
|
setupMockIsLogged(testCase.isLoggedIn);
|
||||||
expect(usePageChangeEffectNavigateLocation()).toEqual(testCase.res);
|
expect(usePageChangeEffectNavigateLocation()).toEqual(testCase.res);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('tests should be exhaustive', () => {
|
describe('tests should be exhaustive', () => {
|
||||||
it('all location and onboarding status should be tested', () => {
|
it('all location, onboarding status and suspended/not suspended workspace activation status should be tested', () => {
|
||||||
const untestedSubscriptionStatus = [
|
|
||||||
SubscriptionStatus.Incomplete,
|
|
||||||
SubscriptionStatus.IncompleteExpired,
|
|
||||||
SubscriptionStatus.Paused,
|
|
||||||
SubscriptionStatus.Trialing,
|
|
||||||
];
|
|
||||||
expect(testCases.length).toEqual(
|
expect(testCases.length).toEqual(
|
||||||
(Object.keys(AppPath).length - UNTESTED_APP_PATHS.length) *
|
(Object.keys(AppPath).length - UNTESTED_APP_PATHS.length) *
|
||||||
(Object.keys(OnboardingStatus).length +
|
(Object.keys(OnboardingStatus).length +
|
||||||
(Object.keys(SubscriptionStatus).length -
|
['isWorkspaceSuspended:true', 'isWorkspaceSuspended:false'].length),
|
||||||
untestedSubscriptionStatus.length)),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,15 +3,15 @@ import { useDefaultHomePagePath } from '@/navigation/hooks/useDefaultHomePagePat
|
|||||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||||
import { AppPath } from '@/types/AppPath';
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
|
||||||
import { OnboardingStatus, SubscriptionStatus } from '~/generated/graphql';
|
import { OnboardingStatus } from '~/generated/graphql';
|
||||||
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
|
||||||
|
|
||||||
export const usePageChangeEffectNavigateLocation = () => {
|
export const usePageChangeEffectNavigateLocation = () => {
|
||||||
const isMatchingLocation = useIsMatchingLocation();
|
const isMatchingLocation = useIsMatchingLocation();
|
||||||
const isLoggedIn = useIsLogged();
|
const isLoggedIn = useIsLogged();
|
||||||
const onboardingStatus = useOnboardingStatus();
|
const onboardingStatus = useOnboardingStatus();
|
||||||
const subscriptionStatus = useSubscriptionStatus();
|
const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
|
||||||
const { defaultHomePagePath } = useDefaultHomePagePath();
|
const { defaultHomePagePath } = useDefaultHomePagePath();
|
||||||
|
|
||||||
const isMatchingOpenRoute =
|
const isMatchingOpenRoute =
|
||||||
@ -49,22 +49,7 @@ export const usePageChangeEffectNavigateLocation = () => {
|
|||||||
return AppPath.PlanRequired;
|
return AppPath.PlanRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (isWorkspaceSuspended && !isMatchingLocation(AppPath.SettingsCatchAll)) {
|
||||||
subscriptionStatus === SubscriptionStatus.Unpaid &&
|
|
||||||
!isMatchingLocation(AppPath.SettingsCatchAll)
|
|
||||||
) {
|
|
||||||
return `${AppPath.SettingsCatchAll.replace('/*', '')}/${
|
|
||||||
SettingsPath.Billing
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
subscriptionStatus === SubscriptionStatus.Canceled &&
|
|
||||||
!(
|
|
||||||
isMatchingLocation(AppPath.SettingsCatchAll) ||
|
|
||||||
isMatchingLocation(AppPath.PlanRequired)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return `${AppPath.SettingsCatchAll.replace('/*', '')}/${
|
return `${AppPath.SettingsCatchAll.replace('/*', '')}/${
|
||||||
SettingsPath.Billing
|
SettingsPath.Billing
|
||||||
}`;
|
}`;
|
||||||
@ -99,14 +84,6 @@ export const usePageChangeEffectNavigateLocation = () => {
|
|||||||
return AppPath.InviteTeam;
|
return AppPath.InviteTeam;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
onboardingStatus === OnboardingStatus.Completed &&
|
|
||||||
subscriptionStatus === SubscriptionStatus.Canceled &&
|
|
||||||
isMatchingLocation(AppPath.PlanRequired)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
onboardingStatus === OnboardingStatus.Completed &&
|
onboardingStatus === OnboardingStatus.Completed &&
|
||||||
isMatchingOnboardingRoute &&
|
isMatchingOnboardingRoute &&
|
||||||
|
|||||||
@ -19,7 +19,6 @@ describe('useSignInWithGoogle', () => {
|
|||||||
plan: BillingPlanKey.Pro,
|
plan: BillingPlanKey.Pro,
|
||||||
interval: SubscriptionInterval.Month,
|
interval: SubscriptionInterval.Month,
|
||||||
requirePaymentMethod: true,
|
requirePaymentMethod: true,
|
||||||
skipPlanPage: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
const Wrapper = getJestMetadataAndApolloMocksWrapper({
|
||||||
@ -31,7 +30,7 @@ describe('useSignInWithGoogle', () => {
|
|||||||
const mockUseParams = { workspaceInviteHash: 'testHash' };
|
const mockUseParams = { workspaceInviteHash: 'testHash' };
|
||||||
|
|
||||||
const mockSearchParams = new URLSearchParams(
|
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);
|
(useParams as jest.Mock).mockReturnValue(mockUseParams);
|
||||||
|
|||||||
@ -22,7 +22,6 @@ describe('useSignInWithMicrosoft', () => {
|
|||||||
plan: 'PRO',
|
plan: 'PRO',
|
||||||
interval: 'Month',
|
interval: 'Month',
|
||||||
requirePaymentMethod: true,
|
requirePaymentMethod: true,
|
||||||
skipPlanPage: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should call signInWithMicrosoft with the correct parameters', () => {
|
it('should call signInWithMicrosoft with the correct parameters', () => {
|
||||||
|
|||||||
@ -12,7 +12,6 @@ export const useSignInWithGoogle = () => {
|
|||||||
plan: 'PRO',
|
plan: 'PRO',
|
||||||
interval: 'Month',
|
interval: 'Month',
|
||||||
requirePaymentMethod: true,
|
requirePaymentMethod: true,
|
||||||
skipPlanPage: false,
|
|
||||||
} as BillingCheckoutSession;
|
} as BillingCheckoutSession;
|
||||||
|
|
||||||
const { signInWithGoogle } = useAuth();
|
const { signInWithGoogle } = useAuth();
|
||||||
|
|||||||
@ -1,16 +1,11 @@
|
|||||||
import { BillingCheckoutSession } from '@/auth/types/billingCheckoutSession.type';
|
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 { createState } from '@ui/utilities/state/utils/createState';
|
||||||
import { syncEffect } from 'recoil-sync';
|
import { syncEffect } from 'recoil-sync';
|
||||||
import { BillingPlanKey, SubscriptionInterval } from '~/generated/graphql';
|
|
||||||
|
|
||||||
export const billingCheckoutSessionState = createState<BillingCheckoutSession>({
|
export const billingCheckoutSessionState = createState<BillingCheckoutSession>({
|
||||||
key: 'billingCheckoutSessionState',
|
key: 'billingCheckoutSessionState',
|
||||||
defaultValue: {
|
defaultValue: BILLING_CHECKOUT_SESSION_DEFAULT_VALUE,
|
||||||
plan: BillingPlanKey.Pro,
|
|
||||||
interval: SubscriptionInterval.Month,
|
|
||||||
requirePaymentMethod: true,
|
|
||||||
skipPlanPage: false,
|
|
||||||
},
|
|
||||||
effects: [
|
effects: [
|
||||||
syncEffect({
|
syncEffect({
|
||||||
refine: (value: unknown) => {
|
refine: (value: unknown) => {
|
||||||
@ -19,8 +14,7 @@ export const billingCheckoutSessionState = createState<BillingCheckoutSession>({
|
|||||||
value !== null &&
|
value !== null &&
|
||||||
'plan' in value &&
|
'plan' in value &&
|
||||||
'interval' in value &&
|
'interval' in value &&
|
||||||
'requirePaymentMethod' in value &&
|
'requirePaymentMethod' in value
|
||||||
'skipPlanPage' in value
|
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
type: 'success',
|
type: 'success',
|
||||||
|
|||||||
@ -5,5 +5,4 @@ export type BillingCheckoutSession = {
|
|||||||
plan: BillingPlanKey;
|
plan: BillingPlanKey;
|
||||||
interval: SubscriptionInterval;
|
interval: SubscriptionInterval;
|
||||||
requirePaymentMethod: boolean;
|
requirePaymentMethod: boolean;
|
||||||
skipPlanPage: boolean;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 (
|
|
||||||
<StyledSubscriptionCardContainer>
|
|
||||||
<StyledTypeContainer>{capitalize(type || '')}</StyledTypeContainer>
|
|
||||||
<SubscriptionCardPrice price={price} />
|
|
||||||
<StyledInfoContainer>{info}</StyledInfoContainer>
|
|
||||||
</StyledSubscriptionCardContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -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 (
|
|
||||||
<StyledSubscriptionCardPriceContainer>
|
|
||||||
<StyledPriceSpan>${price}</StyledPriceSpan>
|
|
||||||
<StyledSeatSpan>/</StyledSeatSpan>
|
|
||||||
<StyledSeatSpan>seat</StyledSeatSpan>
|
|
||||||
</StyledSubscriptionCardPriceContainer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@ -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 (
|
||||||
|
<>
|
||||||
|
<StyledPriceSpan>{`$${price}`}</StyledPriceSpan>
|
||||||
|
<StyledPriceUnitSpan>{`seat / ${type.toLocaleLowerCase()}`}</StyledPriceUnitSpan>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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 (
|
||||||
|
<StyledTrialCardContainer>
|
||||||
|
<StyledTrialDurationContainer>{`${duration} days trial`}</StyledTrialDurationContainer>
|
||||||
|
<StyledCreditCardRequirementContainer>{`${withCreditCard ? 'With Credit Card' : 'Without Credit Card'}`}</StyledCreditCardRequirementContainer>
|
||||||
|
</StyledTrialCardContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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,
|
||||||
|
};
|
||||||
@ -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 };
|
||||||
|
};
|
||||||
@ -6,7 +6,10 @@ export const GET_CLIENT_CONFIG = gql`
|
|||||||
billing {
|
billing {
|
||||||
isBillingEnabled
|
isBillingEnabled
|
||||||
billingUrl
|
billingUrl
|
||||||
billingFreeTrialDurationInDays
|
trialPeriods {
|
||||||
|
duration
|
||||||
|
isCreditCardRequired
|
||||||
|
}
|
||||||
}
|
}
|
||||||
authProviders {
|
authProviders {
|
||||||
google
|
google
|
||||||
|
|||||||
@ -17,12 +17,14 @@ export const InformationBanner = ({
|
|||||||
buttonTitle,
|
buttonTitle,
|
||||||
buttonIcon,
|
buttonIcon,
|
||||||
buttonOnClick,
|
buttonOnClick,
|
||||||
|
isButtonDisabled = false,
|
||||||
}: {
|
}: {
|
||||||
message: string;
|
message: string;
|
||||||
variant?: BannerVariant;
|
variant?: BannerVariant;
|
||||||
buttonTitle?: string;
|
buttonTitle?: string;
|
||||||
buttonIcon?: IconComponent;
|
buttonIcon?: IconComponent;
|
||||||
buttonOnClick?: () => void;
|
buttonOnClick?: () => void;
|
||||||
|
isButtonDisabled?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<StyledBanner variant={variant}>
|
<StyledBanner variant={variant}>
|
||||||
@ -35,6 +37,7 @@ export const InformationBanner = ({
|
|||||||
size="small"
|
size="small"
|
||||||
inverted
|
inverted
|
||||||
onClick={buttonOnClick}
|
onClick={buttonOnClick}
|
||||||
|
disabled={isButtonDisabled}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</StyledBanner>
|
</StyledBanner>
|
||||||
|
|||||||
@ -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 { InformationBannerReconnectAccountEmailAliases } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases';
|
||||||
import { InformationBannerReconnectAccountInsufficientPermissions } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions';
|
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 styled from '@emotion/styled';
|
||||||
|
import { isDefined } from 'twenty-ui';
|
||||||
|
import { SubscriptionStatus } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
const StyledInformationBannerWrapper = styled.div`
|
const StyledInformationBannerWrapper = styled.div`
|
||||||
height: 40px;
|
height: 40px;
|
||||||
@ -12,10 +19,30 @@ const StyledInformationBannerWrapper = styled.div`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const InformationBannerWrapper = () => {
|
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 (
|
return (
|
||||||
<StyledInformationBannerWrapper>
|
<StyledInformationBannerWrapper>
|
||||||
<InformationBannerReconnectAccountInsufficientPermissions />
|
<InformationBannerReconnectAccountInsufficientPermissions />
|
||||||
<InformationBannerReconnectAccountEmailAliases />
|
<InformationBannerReconnectAccountEmailAliases />
|
||||||
|
{displayBillingSubscriptionPausedBanner && (
|
||||||
|
<InformationBannerBillingSubscriptionPaused />
|
||||||
|
)}
|
||||||
|
{displayBillingSubscriptionCanceledBanner && (
|
||||||
|
<InformationBannerNoBillingSubscription />
|
||||||
|
)}
|
||||||
|
{displayFailPaymentInfoBanner && <InformationBannerFailPaymentInfo />}
|
||||||
</StyledInformationBannerWrapper>
|
</StyledInformationBannerWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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 (
|
||||||
|
<InformationBanner
|
||||||
|
variant="danger"
|
||||||
|
message={'Trial expired. Please update your billing details'}
|
||||||
|
buttonTitle="Update"
|
||||||
|
buttonOnClick={() => openBillingPortal()}
|
||||||
|
isButtonDisabled={loading || !isDefined(data)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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 (
|
||||||
|
<InformationBanner
|
||||||
|
variant="danger"
|
||||||
|
message={'Last payment failed. Please update your billing details.'}
|
||||||
|
buttonTitle="Update"
|
||||||
|
buttonOnClick={() => openBillingPortal()}
|
||||||
|
isButtonDisabled={loading || !isDefined(data)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -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 (
|
||||||
|
<InformationBanner
|
||||||
|
variant="danger"
|
||||||
|
message={`Your workspace does not have an active subscription`}
|
||||||
|
buttonTitle="Subscribe"
|
||||||
|
buttonOnClick={() => handleCheckoutSession()}
|
||||||
|
isButtonDisabled={isSubmitting}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -10,11 +10,14 @@ import { PREFETCH_CONFIG } from '@/prefetch/constants/PrefetchConfig';
|
|||||||
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
import { usePrefetchRunQuery } from '@/prefetch/hooks/internal/usePrefetchRunQuery';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
import { View } from '@/views/types/View';
|
import { View } from '@/views/types/View';
|
||||||
|
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const PrefetchRunQueriesEffect = () => {
|
export const PrefetchRunQueriesEffect = () => {
|
||||||
const currentUser = useRecoilValue(currentUserState);
|
const currentUser = useRecoilValue(currentUserState);
|
||||||
|
|
||||||
|
const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
|
||||||
|
|
||||||
const { upsertRecordsInCache: upsertViewsInCache } =
|
const { upsertRecordsInCache: upsertViewsInCache } =
|
||||||
usePrefetchRunQuery<View>({
|
usePrefetchRunQuery<View>({
|
||||||
prefetchKey: PrefetchKey.AllViews,
|
prefetchKey: PrefetchKey.AllViews,
|
||||||
@ -42,7 +45,7 @@ export const PrefetchRunQueriesEffect = () => {
|
|||||||
|
|
||||||
const { result } = useCombinedFindManyRecords({
|
const { result } = useCombinedFindManyRecords({
|
||||||
operationSignatures,
|
operationSignatures,
|
||||||
skip: !currentUser,
|
skip: !currentUser || isWorkspaceSuspended,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
|
import { prefetchIsLoadedFamilyState } from '@/prefetch/states/prefetchIsLoadedFamilyState';
|
||||||
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
import { PrefetchKey } from '@/prefetch/types/PrefetchKey';
|
||||||
|
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
export const useIsPrefetchLoading = () => {
|
export const useIsPrefetchLoading = () => {
|
||||||
|
const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
|
||||||
const isFavoriteFoldersPrefetched = useRecoilValue(
|
const isFavoriteFoldersPrefetched = useRecoilValue(
|
||||||
prefetchIsLoadedFamilyState(PrefetchKey.AllFavoritesFolders),
|
prefetchIsLoadedFamilyState(PrefetchKey.AllFavoritesFolders),
|
||||||
);
|
);
|
||||||
@ -15,8 +17,9 @@ export const useIsPrefetchLoading = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
!areViewsPrefetched ||
|
!isWorkspaceSuspended &&
|
||||||
!areFavoritesPrefetched ||
|
(!areViewsPrefetched ||
|
||||||
!isFavoriteFoldersPrefetched
|
!areFavoritesPrefetched ||
|
||||||
|
!isFavoriteFoldersPrefetched)
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { IconX, UndecoratedLink } from 'twenty-ui';
|
|||||||
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
|
||||||
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
|
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
|
||||||
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
|
||||||
|
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
|
||||||
|
|
||||||
type NavigationDrawerBackButtonProps = {
|
type NavigationDrawerBackButtonProps = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -51,6 +52,11 @@ export const NavigationDrawerBackButton = ({
|
|||||||
navigationDrawerExpandedMemorizedState,
|
navigationDrawerExpandedMemorizedState,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
|
||||||
|
if (isWorkspaceSuspended) {
|
||||||
|
return <StyledContainer />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<UndecoratedLink
|
<UndecoratedLink
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||||
|
import { WorkspaceActivationStatus } from '~/generated/graphql';
|
||||||
|
|
||||||
|
export const useIsWorkspaceActivationStatusSuspended = (): boolean => {
|
||||||
|
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||||
|
return (
|
||||||
|
currentWorkspace?.activationStatus === WorkspaceActivationStatus.Suspended
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -3,50 +3,63 @@ import { Title } from '@/auth/components/Title';
|
|||||||
import { useAuth } from '@/auth/hooks/useAuth';
|
import { useAuth } from '@/auth/hooks/useAuth';
|
||||||
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
|
import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState';
|
||||||
import { SubscriptionBenefit } from '@/billing/components/SubscriptionBenefit';
|
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 { 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 styled from '@emotion/styled';
|
||||||
import { isNonEmptyString, isNumber } from '@sniptt/guards';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||||
import {
|
import {
|
||||||
ActionLink,
|
ActionLink,
|
||||||
CAL_LINK,
|
CAL_LINK,
|
||||||
CardPicker,
|
CardPicker,
|
||||||
|
isDefined,
|
||||||
Loader,
|
Loader,
|
||||||
MainButton,
|
MainButton,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
import {
|
import { SubscriptionInterval } from '~/generated-metadata/graphql';
|
||||||
ProductPriceEntity,
|
import { useGetProductPricesQuery } from '~/generated/graphql';
|
||||||
SubscriptionInterval,
|
|
||||||
useCheckoutSessionMutation,
|
|
||||||
useGetProductPricesQuery,
|
|
||||||
} from '~/generated/graphql';
|
|
||||||
import { isDefined } from '~/utils/isDefined';
|
|
||||||
|
|
||||||
const StyledChoosePlanContainer = styled.div`
|
const StyledSubscriptionContainer = styled.div<{
|
||||||
display: flex;
|
withLongerMarginBottom: boolean;
|
||||||
flex-direction: row;
|
}>`
|
||||||
width: 100%;
|
|
||||||
margin: ${({ theme }) => theme.spacing(8)} 0
|
|
||||||
${({ theme }) => theme.spacing(2)};
|
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
|
||||||
|
|
||||||
const StyledBenefitsContainer = styled.div`
|
|
||||||
background-color: ${({ theme }) => theme.background.secondary};
|
background-color: ${({ theme }) => theme.background.secondary};
|
||||||
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
border: 1px solid ${({ theme }) => theme.border.color.medium};
|
||||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
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;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding: ${({ theme }) => theme.spacing(4)} ${({ theme }) => theme.spacing(3)};
|
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)};
|
margin-bottom: ${({ theme }) => theme.spacing(8)};
|
||||||
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledLinkGroup = styled.div`
|
const StyledLinkGroup = styled.div`
|
||||||
@ -71,58 +84,48 @@ const benefits = [
|
|||||||
'Email integration',
|
'Email integration',
|
||||||
'Custom objects',
|
'Custom objects',
|
||||||
'API & Webhooks',
|
'API & Webhooks',
|
||||||
'Frequent updates',
|
'1 000 workflow node executions',
|
||||||
'And much more',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ChooseYourPlan = () => {
|
export const ChooseYourPlan = () => {
|
||||||
const billing = useRecoilValue(billingState);
|
const billing = useRecoilValue(billingState);
|
||||||
|
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
||||||
|
|
||||||
const { enqueueSnackBar } = useSnackBar();
|
|
||||||
|
|
||||||
const { data: prices } = useGetProductPricesQuery({
|
const { data: prices } = useGetProductPricesQuery({
|
||||||
variables: { product: 'base-plan' },
|
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(
|
const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState(
|
||||||
billingCheckoutSessionState,
|
billingCheckoutSessionState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [checkoutSession] = useCheckoutSessionMutation();
|
const { handleCheckoutSession, isSubmitting } = useHandleCheckoutSession({
|
||||||
|
recurringInterval: billingCheckoutSession.interval,
|
||||||
|
plan: billingCheckoutSession.plan,
|
||||||
|
requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
|
||||||
|
});
|
||||||
|
|
||||||
const handleCheckoutSession = async () => {
|
const handleTrialPeriodChange = (withCreditCard: boolean) => {
|
||||||
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) => {
|
|
||||||
return () => {
|
return () => {
|
||||||
if (isNonEmptyString(type) && billingCheckoutSession.interval !== type) {
|
if (
|
||||||
|
isDefined(price) &&
|
||||||
|
billingCheckoutSession.requirePaymentMethod !== withCreditCard
|
||||||
|
) {
|
||||||
setBillingCheckoutSession({
|
setBillingCheckoutSession({
|
||||||
plan: billingCheckoutSession.plan,
|
plan: billingCheckoutSession.plan,
|
||||||
interval: type,
|
interval: price.recurringInterval,
|
||||||
requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
|
requirePaymentMethod: withCreditCard,
|
||||||
skipPlanPage: false,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -130,65 +133,58 @@ export const ChooseYourPlan = () => {
|
|||||||
|
|
||||||
const { signOut } = useAuth();
|
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 <Loader />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
prices?.getProductPrices?.productPrices && (
|
isDefined(price) &&
|
||||||
|
isDefined(billing) && (
|
||||||
<>
|
<>
|
||||||
<Title noMarginTop>Choose your Plan</Title>
|
<Title noMarginTop>
|
||||||
<SubTitle>
|
{hasWithoutCreditCardTrialPeriod
|
||||||
Enjoy a {billing?.billingFreeTrialDurationInDays}-day free trial
|
? 'Choose your Trial'
|
||||||
</SubTitle>
|
: 'Get your subscription'}
|
||||||
<StyledChoosePlanContainer>
|
</Title>
|
||||||
{prices.getProductPrices.productPrices.map((price, index) => (
|
{hasWithoutCreditCardTrialPeriod ? (
|
||||||
<CardPicker
|
<SubTitle>Cancel anytime</SubTitle>
|
||||||
checked={
|
) : (
|
||||||
price.recurringInterval === billingCheckoutSession.interval
|
withCreditCardTrialPeriod && (
|
||||||
}
|
<SubTitle>{`Enjoy a ${withCreditCardTrialPeriod.duration}-days free trial`}</SubTitle>
|
||||||
handleChange={handleIntervalChange(price.recurringInterval)}
|
)
|
||||||
key={index}
|
)}
|
||||||
>
|
<StyledSubscriptionContainer
|
||||||
<SubscriptionCard
|
withLongerMarginBottom={!hasWithoutCreditCardTrialPeriod}
|
||||||
type={price.recurringInterval}
|
>
|
||||||
price={price.unitAmount / 100}
|
<StyledSubscriptionPriceContainer>
|
||||||
info={computeInfo(price, prices.getProductPrices.productPrices)}
|
<SubscriptionPrice
|
||||||
/>
|
type={price.recurringInterval}
|
||||||
</CardPicker>
|
price={price.unitAmount / 100}
|
||||||
))}
|
/>
|
||||||
</StyledChoosePlanContainer>
|
</StyledSubscriptionPriceContainer>
|
||||||
<StyledBenefitsContainer>
|
<StyledBenefitsContainer>
|
||||||
{benefits.map((benefit, index) => (
|
{benefits.map((benefit) => (
|
||||||
<SubscriptionBenefit key={index}>{benefit}</SubscriptionBenefit>
|
<SubscriptionBenefit key={benefit}>{benefit}</SubscriptionBenefit>
|
||||||
))}
|
))}
|
||||||
</StyledBenefitsContainer>
|
</StyledBenefitsContainer>
|
||||||
|
</StyledSubscriptionContainer>
|
||||||
|
{hasWithoutCreditCardTrialPeriod && (
|
||||||
|
<StyledChooseTrialContainer>
|
||||||
|
{billing.trialPeriods.map((trialPeriod) => (
|
||||||
|
<CardPicker
|
||||||
|
checked={
|
||||||
|
billingCheckoutSession.requirePaymentMethod ===
|
||||||
|
trialPeriod.isCreditCardRequired
|
||||||
|
}
|
||||||
|
handleChange={handleTrialPeriodChange(
|
||||||
|
trialPeriod.isCreditCardRequired,
|
||||||
|
)}
|
||||||
|
key={trialPeriod.duration}
|
||||||
|
>
|
||||||
|
<TrialCard
|
||||||
|
duration={trialPeriod.duration}
|
||||||
|
withCreditCard={trialPeriod.isCreditCardRequired}
|
||||||
|
/>
|
||||||
|
</CardPicker>
|
||||||
|
))}
|
||||||
|
</StyledChooseTrialContainer>
|
||||||
|
)}
|
||||||
<MainButton
|
<MainButton
|
||||||
title="Continue"
|
title="Continue"
|
||||||
onClick={handleCheckoutSession}
|
onClick={handleCheckoutSession}
|
||||||
|
|||||||
@ -40,14 +40,14 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
{
|
{
|
||||||
__typename: 'ProductPriceEntity',
|
__typename: 'ProductPriceEntity',
|
||||||
created: 1699860608,
|
created: 1699860608,
|
||||||
recurringInterval: 'month',
|
recurringInterval: 'Month',
|
||||||
stripePriceId: 'monthly8usd',
|
stripePriceId: 'monthly8usd',
|
||||||
unitAmount: 900,
|
unitAmount: 900,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
__typename: 'ProductPriceEntity',
|
__typename: 'ProductPriceEntity',
|
||||||
created: 1701874964,
|
created: 1701874964,
|
||||||
recurringInterval: 'year',
|
recurringInterval: 'Year',
|
||||||
stripePriceId: 'priceId',
|
stripePriceId: 'priceId',
|
||||||
unitAmount: 9000,
|
unitAmount: 9000,
|
||||||
},
|
},
|
||||||
@ -56,7 +56,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
graphqlMocks.handlers,
|
...graphqlMocks.handlers,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -70,7 +70,7 @@ export const Default: Story = {
|
|||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
await canvas.findByText('Choose your Plan', undefined, {
|
await canvas.findByText('Choose your Trial', undefined, {
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import {
|
|||||||
IconCalendarEvent,
|
IconCalendarEvent,
|
||||||
IconCircleX,
|
IconCircleX,
|
||||||
IconCreditCard,
|
IconCreditCard,
|
||||||
Info,
|
|
||||||
Section,
|
Section,
|
||||||
} from 'twenty-ui';
|
} from 'twenty-ui';
|
||||||
|
|
||||||
@ -15,7 +14,6 @@ import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingC
|
|||||||
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
import { useOnboardingStatus } from '@/onboarding/hooks/useOnboardingStatus';
|
||||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||||
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath';
|
||||||
import { AppPath } from '@/types/AppPath';
|
|
||||||
import { SettingsPath } from '@/types/SettingsPath';
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
|
||||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||||
@ -25,7 +23,6 @@ import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
|
|||||||
import {
|
import {
|
||||||
OnboardingStatus,
|
OnboardingStatus,
|
||||||
SubscriptionInterval,
|
SubscriptionInterval,
|
||||||
SubscriptionStatus,
|
|
||||||
useBillingPortalSessionQuery,
|
useBillingPortalSessionQuery,
|
||||||
useUpdateBillingSubscriptionMutation,
|
useUpdateBillingSubscriptionMutation,
|
||||||
} from '~/generated/graphql';
|
} from '~/generated/graphql';
|
||||||
@ -87,17 +84,6 @@ export const SettingsBilling = () => {
|
|||||||
billingPortalButtonDisabled ||
|
billingPortalButtonDisabled ||
|
||||||
onboardingStatus !== OnboardingStatus.Completed;
|
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 = () => {
|
const openBillingPortal = () => {
|
||||||
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
if (isDefined(data) && isDefined(data.billingPortalSession.url)) {
|
||||||
window.location.replace(data.billingPortalSession.url);
|
window.location.replace(data.billingPortalSession.url);
|
||||||
@ -147,30 +133,7 @@ export const SettingsBilling = () => {
|
|||||||
>
|
>
|
||||||
<SettingsPageContainer>
|
<SettingsPageContainer>
|
||||||
<SettingsBillingCoverImage />
|
<SettingsBillingCoverImage />
|
||||||
{displayPaymentFailInfo && (
|
{isDefined(subscriptionStatus) && (
|
||||||
<Info
|
|
||||||
text={'Last payment failed. Please update your billing details.'}
|
|
||||||
buttonTitle={'Update'}
|
|
||||||
accent={'danger'}
|
|
||||||
onClick={openBillingPortal}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{displaySubscriptionCanceledInfo && (
|
|
||||||
<Info
|
|
||||||
text={'Subscription canceled. Please start a new one'}
|
|
||||||
buttonTitle={'Subscribe'}
|
|
||||||
accent={'danger'}
|
|
||||||
to={AppPath.PlanRequired}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{displaySubscribeInfo ? (
|
|
||||||
<Info
|
|
||||||
text={'Your workspace does not have an active subscription'}
|
|
||||||
buttonTitle={'Subscribe'}
|
|
||||||
accent={'danger'}
|
|
||||||
to={AppPath.PlanRequired}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<>
|
<>
|
||||||
<Section>
|
<Section>
|
||||||
<H2Title
|
<H2Title
|
||||||
|
|||||||
@ -31,7 +31,18 @@ export const mockedClientConfig: ClientConfig = {
|
|||||||
billing: {
|
billing: {
|
||||||
isBillingEnabled: true,
|
isBillingEnabled: true,
|
||||||
billingUrl: '',
|
billingUrl: '',
|
||||||
billingFreeTrialDurationInDays: 10,
|
trialPeriods: [
|
||||||
|
{
|
||||||
|
__typename: 'TrialPeriodDTO',
|
||||||
|
duration: 30,
|
||||||
|
isCreditCardRequired: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
__typename: 'TrialPeriodDTO',
|
||||||
|
duration: 7,
|
||||||
|
isCreditCardRequired: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
__typename: 'Billing',
|
__typename: 'Billing',
|
||||||
},
|
},
|
||||||
captcha: {
|
captcha: {
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
import { Field, ObjectType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
import { Min } from 'class-validator';
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
export class TrialPeriodDTO {
|
||||||
|
@Field(() => Number)
|
||||||
|
@Min(0)
|
||||||
|
duration: number;
|
||||||
|
|
||||||
|
@Field(() => Boolean)
|
||||||
|
isCreditCardRequired: boolean;
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
import { isDefined } from 'class-validator';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
import { BillingSubscription } from 'src/engine/core-modules/billing/entities/billing-subscription.entity';
|
||||||
@ -50,15 +51,15 @@ export class BillingPortalWorkspaceService {
|
|||||||
workspaceId: workspace.id,
|
workspaceId: workspace.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
const stripeCustomerId = (
|
const subscription = await this.billingSubscriptionRepository.findOneBy({
|
||||||
await this.billingSubscriptionRepository.findOneBy({
|
workspaceId: workspace.id,
|
||||||
workspaceId: workspace.id,
|
});
|
||||||
})
|
|
||||||
)?.stripeCustomerId;
|
|
||||||
|
|
||||||
const session = await this.stripeCheckoutService.createCheckoutSession(
|
const stripeCustomerId = subscription?.stripeCustomerId;
|
||||||
|
|
||||||
|
const session = await this.stripeCheckoutService.createCheckoutSession({
|
||||||
user,
|
user,
|
||||||
workspace.id,
|
workspaceId: workspace.id,
|
||||||
priceId,
|
priceId,
|
||||||
quantity,
|
quantity,
|
||||||
successUrl,
|
successUrl,
|
||||||
@ -66,7 +67,8 @@ export class BillingPortalWorkspaceService {
|
|||||||
stripeCustomerId,
|
stripeCustomerId,
|
||||||
plan,
|
plan,
|
||||||
requirePaymentMethod,
|
requirePaymentMethod,
|
||||||
);
|
withTrialPeriod: !isDefined(subscription),
|
||||||
|
});
|
||||||
|
|
||||||
assert(session.url, 'Error: missing checkout.session.url');
|
assert(session.url, 'Error: missing checkout.session.url');
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { isDefined } from 'class-validator';
|
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 { 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 { BillingSubscriptionService } from 'src/engine/core-modules/billing/services/billing-subscription.service';
|
||||||
import { EnvironmentService } from 'src/engine/core-modules/environment/environment.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';
|
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 environmentService: EnvironmentService,
|
||||||
private readonly billingSubscriptionService: BillingSubscriptionService,
|
private readonly billingSubscriptionService: BillingSubscriptionService,
|
||||||
private readonly isFeatureEnabledService: FeatureFlagService,
|
private readonly isFeatureEnabledService: FeatureFlagService,
|
||||||
|
@InjectRepository(BillingSubscription, 'core')
|
||||||
|
private readonly billingSubscriptionRepository: Repository<BillingSubscription>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
isBillingEnabled() {
|
isBillingEnabled() {
|
||||||
return this.environmentService.get('IS_BILLING_ENABLED');
|
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,
|
workspaceId: string,
|
||||||
entitlementKey?: BillingEntitlementKey,
|
entitlementKey: BillingEntitlementKey,
|
||||||
) {
|
) {
|
||||||
const isBillingEnabled = this.isBillingEnabled();
|
const isBillingEnabled = this.isBillingEnabled();
|
||||||
|
|
||||||
@ -42,25 +70,9 @@ export class BillingService {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entitlementKey) {
|
return this.billingSubscriptionService.getWorkspaceEntitlementByKey(
|
||||||
return this.billingSubscriptionService.getWorkspaceEntitlementByKey(
|
workspaceId,
|
||||||
workspaceId,
|
entitlementKey,
|
||||||
entitlementKey,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentBillingSubscription =
|
|
||||||
await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow(
|
|
||||||
{ workspaceId },
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
isDefined(currentBillingSubscription) &&
|
|
||||||
[
|
|
||||||
SubscriptionStatus.Active,
|
|
||||||
SubscriptionStatus.Trialing,
|
|
||||||
SubscriptionStatus.PastDue,
|
|
||||||
].includes(currentBillingSubscription.status)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,17 +24,29 @@ export class StripeCheckoutService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createCheckoutSession(
|
async createCheckoutSession({
|
||||||
user: User,
|
user,
|
||||||
workspaceId: string,
|
workspaceId,
|
||||||
priceId: string,
|
priceId,
|
||||||
quantity: number,
|
quantity,
|
||||||
successUrl?: string,
|
successUrl,
|
||||||
cancelUrl?: string,
|
cancelUrl,
|
||||||
stripeCustomerId?: string,
|
stripeCustomerId,
|
||||||
plan: BillingPlanKey = BillingPlanKey.PRO,
|
plan = BillingPlanKey.PRO,
|
||||||
requirePaymentMethod = true,
|
requirePaymentMethod = true,
|
||||||
): Promise<Stripe.Checkout.Session> {
|
withTrialPeriod,
|
||||||
|
}: {
|
||||||
|
user: User;
|
||||||
|
workspaceId: string;
|
||||||
|
priceId: string;
|
||||||
|
quantity: number;
|
||||||
|
successUrl?: string;
|
||||||
|
cancelUrl?: string;
|
||||||
|
stripeCustomerId?: string;
|
||||||
|
plan?: BillingPlanKey;
|
||||||
|
requirePaymentMethod?: boolean;
|
||||||
|
withTrialPeriod: boolean;
|
||||||
|
}): Promise<Stripe.Checkout.Session> {
|
||||||
return await this.stripe.checkout.sessions.create({
|
return await this.stripe.checkout.sessions.create({
|
||||||
line_items: [
|
line_items: [
|
||||||
{
|
{
|
||||||
@ -48,14 +60,25 @@ export class StripeCheckoutService {
|
|||||||
workspaceId,
|
workspaceId,
|
||||||
plan,
|
plan,
|
||||||
},
|
},
|
||||||
trial_period_days: this.environmentService.get(
|
...(withTrialPeriod
|
||||||
'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
|
? {
|
||||||
),
|
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 },
|
automatic_tax: { enabled: !!requirePaymentMethod },
|
||||||
tax_id_collection: { enabled: !!requirePaymentMethod },
|
tax_id_collection: { enabled: !!requirePaymentMethod },
|
||||||
customer: stripeCustomerId,
|
customer: stripeCustomerId,
|
||||||
customer_update: stripeCustomerId ? { name: 'auto' } : undefined,
|
customer_update: stripeCustomerId
|
||||||
|
? { name: 'auto', address: 'auto' }
|
||||||
|
: undefined,
|
||||||
customer_email: stripeCustomerId ? undefined : user.email,
|
customer_email: stripeCustomerId ? undefined : user.email,
|
||||||
success_url: successUrl,
|
success_url: successUrl,
|
||||||
cancel_url: cancelUrl,
|
cancel_url: cancelUrl,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { Field, ObjectType } from '@nestjs/graphql';
|
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 { CaptchaDriverType } from 'src/engine/core-modules/captcha/interfaces';
|
||||||
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output';
|
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output';
|
||||||
|
|
||||||
@ -11,8 +12,8 @@ class Billing {
|
|||||||
@Field(() => String, { nullable: true })
|
@Field(() => String, { nullable: true })
|
||||||
billingUrl?: string;
|
billingUrl?: string;
|
||||||
|
|
||||||
@Field(() => Number, { nullable: true })
|
@Field(() => [TrialPeriodDTO])
|
||||||
billingFreeTrialDurationInDays?: number;
|
trialPeriods: TrialPeriodDTO[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
|
|||||||
@ -18,9 +18,20 @@ export class ClientConfigResolver {
|
|||||||
billing: {
|
billing: {
|
||||||
isBillingEnabled: this.environmentService.get('IS_BILLING_ENABLED'),
|
isBillingEnabled: this.environmentService.get('IS_BILLING_ENABLED'),
|
||||||
billingUrl: this.environmentService.get('BILLING_PLAN_REQUIRED_LINK'),
|
billingUrl: this.environmentService.get('BILLING_PLAN_REQUIRED_LINK'),
|
||||||
billingFreeTrialDurationInDays: this.environmentService.get(
|
trialPeriods: [
|
||||||
'BILLING_FREE_TRIAL_DURATION_IN_DAYS',
|
{
|
||||||
),
|
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: {
|
authProviders: {
|
||||||
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
|
google: this.environmentService.get('AUTH_GOOGLE_ENABLED'),
|
||||||
|
|||||||
@ -73,7 +73,13 @@ export class EnvironmentVariables {
|
|||||||
@CastToPositiveNumber()
|
@CastToPositiveNumber()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@ValidateIf((env) => env.IS_BILLING_ENABLED === true)
|
@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()
|
@IsString()
|
||||||
@ValidateIf((env) => env.IS_BILLING_ENABLED === true)
|
@ValidateIf((env) => env.IS_BILLING_ENABLED === true)
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export class OnboardingService {
|
|||||||
|
|
||||||
private async isSubscriptionIncompleteOnboardingStatus(workspace: Workspace) {
|
private async isSubscriptionIncompleteOnboardingStatus(workspace: Workspace) {
|
||||||
const hasSubscription =
|
const hasSubscription =
|
||||||
await this.billingService.hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement(
|
await this.billingService.hasWorkspaceSubscriptionOrFreeAccess(
|
||||||
workspace.id,
|
workspace.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ export class SSOService {
|
|||||||
|
|
||||||
private async isSSOEnabled(workspaceId: string) {
|
private async isSSOEnabled(workspaceId: string) {
|
||||||
const isSSOBillingEnabled =
|
const isSSOBillingEnabled =
|
||||||
await this.billingService.hasWorkspaceActiveSubscriptionOrFreeAccessOrEntitlement(
|
await this.billingService.hasFreeAccessOrEntitlement(
|
||||||
workspaceId,
|
workspaceId,
|
||||||
this.featureLookUpKey,
|
this.featureLookUpKey,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -41,7 +41,10 @@ export class UserService extends TypeOrmQueryService<User> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadWorkspaceMember(user: User, workspace: Workspace) {
|
async loadWorkspaceMember(user: User, workspace: Workspace) {
|
||||||
if (workspace?.activationStatus !== WorkspaceActivationStatus.ACTIVE) {
|
if (
|
||||||
|
workspace?.activationStatus !== WorkspaceActivationStatus.ACTIVE &&
|
||||||
|
workspace?.activationStatus !== WorkspaceActivationStatus.SUSPENDED
|
||||||
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user