Fix sentry issue (#6719)
https://twenty-v7.sentry.io/issues/5677123076/?environment=prod&project=4507072499810304&query=is%3Aunresolved+issue.priority%3A%5Bhigh%2C+medium%5D&referrer=issue-stream&statsPeriod=7d&stream_index=12 Removes billing section when is_free_access_enabled
This commit is contained in:
@ -3,7 +3,6 @@ import {
|
|||||||
createBrowserRouter,
|
createBrowserRouter,
|
||||||
createRoutesFromElements,
|
createRoutesFromElements,
|
||||||
Outlet,
|
Outlet,
|
||||||
redirect,
|
|
||||||
Route,
|
Route,
|
||||||
RouterProvider,
|
RouterProvider,
|
||||||
Routes,
|
Routes,
|
||||||
@ -192,14 +191,12 @@ const createRouter = (
|
|||||||
path={SettingsPath.AccountsEmails}
|
path={SettingsPath.AccountsEmails}
|
||||||
element={<SettingsAccountsEmails />}
|
element={<SettingsAccountsEmails />}
|
||||||
/>
|
/>
|
||||||
<Route
|
{isBillingEnabled && (
|
||||||
path={SettingsPath.Billing}
|
<Route
|
||||||
element={<SettingsBilling />}
|
path={SettingsPath.Billing}
|
||||||
loader={() => {
|
element={<SettingsBilling />}
|
||||||
if (!isBillingEnabled) return redirect(AppPath.Index);
|
/>
|
||||||
return null;
|
)}
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
path={SettingsPath.WorkspaceMembersPage}
|
path={SettingsPath.WorkspaceMembersPage}
|
||||||
element={<SettingsWorkspaceMembers />}
|
element={<SettingsWorkspaceMembers />}
|
||||||
@ -324,15 +321,19 @@ const createRouter = (
|
|||||||
|
|
||||||
export const App = () => {
|
export const App = () => {
|
||||||
const billing = useRecoilValue(billingState);
|
const billing = useRecoilValue(billingState);
|
||||||
|
const isFreeAccessEnabled = useIsFeatureEnabled('IS_FREE_ACCESS_ENABLED');
|
||||||
const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED');
|
const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED');
|
||||||
const isServerlessFunctionSettingsEnabled = useIsFeatureEnabled(
|
const isServerlessFunctionSettingsEnabled = useIsFeatureEnabled(
|
||||||
'IS_FUNCTION_SETTINGS_ENABLED',
|
'IS_FUNCTION_SETTINGS_ENABLED',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const isBillingPageEnabled =
|
||||||
|
billing?.isBillingEnabled && !isFreeAccessEnabled;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RouterProvider
|
<RouterProvider
|
||||||
router={createRouter(
|
router={createRouter(
|
||||||
billing?.isBillingEnabled,
|
isBillingPageEnabled,
|
||||||
isCRMMigrationEnabled,
|
isCRMMigrationEnabled,
|
||||||
isServerlessFunctionSettingsEnabled,
|
isServerlessFunctionSettingsEnabled,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -215,6 +215,15 @@ export type ExchangeAuthCode = {
|
|||||||
refreshToken: AuthToken;
|
refreshToken: AuthToken;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ExecuteServerlessFunctionInput = {
|
||||||
|
/** Id of the serverless function to execute */
|
||||||
|
id: Scalars['UUID'];
|
||||||
|
/** Payload in JSON format */
|
||||||
|
payload?: InputMaybe<Scalars['JSON']>;
|
||||||
|
/** Version of the serverless function to execute */
|
||||||
|
version?: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type FeatureFlag = {
|
export type FeatureFlag = {
|
||||||
__typename?: 'FeatureFlag';
|
__typename?: 'FeatureFlag';
|
||||||
id: Scalars['UUID'];
|
id: Scalars['UUID'];
|
||||||
@ -287,6 +296,13 @@ export type FullName = {
|
|||||||
lastName: Scalars['String'];
|
lastName: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GetServerlessFunctionSourceCodeInput = {
|
||||||
|
/** The id of the function. */
|
||||||
|
id: Scalars['ID'];
|
||||||
|
/** The version of the function */
|
||||||
|
version?: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
export type InvalidatePassword = {
|
export type InvalidatePassword = {
|
||||||
__typename?: 'InvalidatePassword';
|
__typename?: 'InvalidatePassword';
|
||||||
/** Boolean that confirms query was dispatched */
|
/** Boolean that confirms query was dispatched */
|
||||||
@ -343,8 +359,9 @@ export type Mutation = {
|
|||||||
generateJWT: AuthTokens;
|
generateJWT: AuthTokens;
|
||||||
generateTransientToken: TransientToken;
|
generateTransientToken: TransientToken;
|
||||||
impersonate: Verify;
|
impersonate: Verify;
|
||||||
|
publishServerlessFunction: ServerlessFunction;
|
||||||
renewToken: AuthTokens;
|
renewToken: AuthTokens;
|
||||||
runWorkflowVersion: WorkflowTriggerResult;
|
runWorkflowVersion: WorkflowRun;
|
||||||
sendInviteLink: SendInviteLink;
|
sendInviteLink: SendInviteLink;
|
||||||
signUp: LoginToken;
|
signUp: LoginToken;
|
||||||
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
skipSyncEmailOnboardingStep: OnboardingStepSuccess;
|
||||||
@ -431,8 +448,7 @@ export type MutationExchangeAuthorizationCodeArgs = {
|
|||||||
|
|
||||||
|
|
||||||
export type MutationExecuteOneServerlessFunctionArgs = {
|
export type MutationExecuteOneServerlessFunctionArgs = {
|
||||||
id: Scalars['UUID'];
|
input: ExecuteServerlessFunctionInput;
|
||||||
payload?: InputMaybe<Scalars['JSON']>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -452,6 +468,11 @@ export type MutationImpersonateArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type MutationPublishServerlessFunctionArgs = {
|
||||||
|
input: PublishServerlessFunctionInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type MutationRenewTokenArgs = {
|
export type MutationRenewTokenArgs = {
|
||||||
appToken: Scalars['String'];
|
appToken: Scalars['String'];
|
||||||
};
|
};
|
||||||
@ -594,6 +615,11 @@ export type ProductPricesEntity = {
|
|||||||
totalNumberOfPrices: Scalars['Int'];
|
totalNumberOfPrices: Scalars['Int'];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PublishServerlessFunctionInput = {
|
||||||
|
/** The id of the function. */
|
||||||
|
id: Scalars['ID'];
|
||||||
|
};
|
||||||
|
|
||||||
export type Query = {
|
export type Query = {
|
||||||
__typename?: 'Query';
|
__typename?: 'Query';
|
||||||
billingPortalSession: SessionEntity;
|
billingPortalSession: SessionEntity;
|
||||||
@ -606,6 +632,7 @@ export type Query = {
|
|||||||
getAISQLQuery: AisqlQueryResult;
|
getAISQLQuery: AisqlQueryResult;
|
||||||
getPostgresCredentials?: Maybe<PostgresCredentials>;
|
getPostgresCredentials?: Maybe<PostgresCredentials>;
|
||||||
getProductPrices: ProductPricesEntity;
|
getProductPrices: ProductPricesEntity;
|
||||||
|
getServerlessFunctionSourceCode: Scalars['String'];
|
||||||
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
|
getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal;
|
||||||
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
|
getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal;
|
||||||
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
|
getTimelineThreadsFromCompanyId: TimelineThreadsWithTotal;
|
||||||
@ -649,6 +676,11 @@ export type QueryGetProductPricesArgs = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export type QueryGetServerlessFunctionSourceCodeArgs = {
|
||||||
|
input: GetServerlessFunctionSourceCodeInput;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = {
|
export type QueryGetTimelineCalendarEventsFromCompanyIdArgs = {
|
||||||
companyId: Scalars['UUID'];
|
companyId: Scalars['UUID'];
|
||||||
page: Scalars['Int'];
|
page: Scalars['Int'];
|
||||||
@ -768,9 +800,9 @@ export type ServerlessFunction = {
|
|||||||
createdAt: Scalars['DateTime'];
|
createdAt: Scalars['DateTime'];
|
||||||
description?: Maybe<Scalars['String']>;
|
description?: Maybe<Scalars['String']>;
|
||||||
id: Scalars['UUID'];
|
id: Scalars['UUID'];
|
||||||
|
latestVersion?: Maybe<Scalars['String']>;
|
||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
runtime: Scalars['String'];
|
runtime: Scalars['String'];
|
||||||
sourceCodeFullPath: Scalars['String'];
|
|
||||||
sourceCodeHash: Scalars['String'];
|
sourceCodeHash: Scalars['String'];
|
||||||
syncStatus: ServerlessFunctionSyncStatus;
|
syncStatus: ServerlessFunctionSyncStatus;
|
||||||
updatedAt: Scalars['DateTime'];
|
updatedAt: Scalars['DateTime'];
|
||||||
@ -1054,10 +1086,9 @@ export type Verify = {
|
|||||||
user: User;
|
user: User;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type WorkflowTriggerResult = {
|
export type WorkflowRun = {
|
||||||
__typename?: 'WorkflowTriggerResult';
|
__typename?: 'WorkflowRun';
|
||||||
/** Execution result in JSON format */
|
workflowRunId: Scalars['UUID'];
|
||||||
result?: Maybe<Scalars['JSON']>;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Workspace = {
|
export type Workspace = {
|
||||||
|
|||||||
@ -33,7 +33,10 @@ export const SettingsNavigationDrawerItems = () => {
|
|||||||
const isFunctionSettingsEnabled = useIsFeatureEnabled(
|
const isFunctionSettingsEnabled = useIsFeatureEnabled(
|
||||||
'IS_FUNCTION_SETTINGS_ENABLED',
|
'IS_FUNCTION_SETTINGS_ENABLED',
|
||||||
);
|
);
|
||||||
|
const isFreeAccessEnabled = useIsFeatureEnabled('IS_FREE_ACCESS_ENABLED');
|
||||||
const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED');
|
const isCRMMigrationEnabled = useIsFeatureEnabled('IS_CRM_MIGRATION_ENABLED');
|
||||||
|
const isBillingPageEnabled =
|
||||||
|
billing?.isBillingEnabled && !isFreeAccessEnabled;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -84,7 +87,7 @@ export const SettingsNavigationDrawerItems = () => {
|
|||||||
path={SettingsPath.WorkspaceMembersPage}
|
path={SettingsPath.WorkspaceMembersPage}
|
||||||
Icon={IconUsers}
|
Icon={IconUsers}
|
||||||
/>
|
/>
|
||||||
{billing?.isBillingEnabled && (
|
{isBillingPageEnabled && (
|
||||||
<SettingsNavigationDrawerItem
|
<SettingsNavigationDrawerItem
|
||||||
label="Billing"
|
label="Billing"
|
||||||
path={SettingsPath.Billing}
|
path={SettingsPath.Billing}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ describe('useServerlessFunctionUpdateFormState', () => {
|
|||||||
);
|
);
|
||||||
useGetOneServerlessFunctionMock.useGetOneServerlessFunction.mockReturnValue(
|
useGetOneServerlessFunctionMock.useGetOneServerlessFunction.mockReturnValue(
|
||||||
{
|
{
|
||||||
serverlessFunction: { sourceCodeFullPath: undefined },
|
serverlessFunction: { name: 'name' },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
const useGetOneServerlessFunctionSourceCodeMock = jest.requireMock(
|
const useGetOneServerlessFunctionSourceCodeMock = jest.requireMock(
|
||||||
|
|||||||
@ -7,4 +7,5 @@ export type FeatureFlagKey =
|
|||||||
| 'IS_FUNCTION_SETTINGS_ENABLED'
|
| 'IS_FUNCTION_SETTINGS_ENABLED'
|
||||||
| 'IS_COPILOT_ENABLED'
|
| 'IS_COPILOT_ENABLED'
|
||||||
| 'IS_CRM_MIGRATION_ENABLED'
|
| 'IS_CRM_MIGRATION_ENABLED'
|
||||||
|
| 'IS_FREE_ACCESS_ENABLED'
|
||||||
| 'IS_MESSAGE_THREAD_SUBSCRIBER_ENABLED';
|
| 'IS_MESSAGE_THREAD_SUBSCRIBER_ENABLED';
|
||||||
|
|||||||
@ -38,7 +38,6 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
description: '',
|
description: '',
|
||||||
syncStatus: 'READY',
|
syncStatus: 'READY',
|
||||||
runtime: 'nodejs18.x',
|
runtime: 'nodejs18.x',
|
||||||
sourceCodeFullPath: SOURCE_CODE_FULL_PATH,
|
|
||||||
sourceCodeHash: '42d2734b3dc8a7b45a16803ed7f417bc',
|
sourceCodeHash: '42d2734b3dc8a7b45a16803ed7f417bc',
|
||||||
updatedAt: '2024-02-24T10:23:10.673Z',
|
updatedAt: '2024-02-24T10:23:10.673Z',
|
||||||
createdAt: '2024-02-24T10:23:10.673Z',
|
createdAt: '2024-02-24T10:23:10.673Z',
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export class BillingResolver {
|
|||||||
@Args() { returnUrlPath }: BillingSessionInput,
|
@Args() { returnUrlPath }: BillingSessionInput,
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
url: await this.billingPortalWorkspaceService.computeBillingPortalSessionURL(
|
url: await this.billingPortalWorkspaceService.computeBillingPortalSessionURLOrThrow(
|
||||||
user.defaultWorkspaceId,
|
user.defaultWorkspaceId,
|
||||||
returnUrlPath,
|
returnUrlPath,
|
||||||
),
|
),
|
||||||
|
|||||||
@ -65,18 +65,22 @@ export class BillingPortalWorkspaceService {
|
|||||||
return session.url;
|
return session.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
async computeBillingPortalSessionURL(
|
async computeBillingPortalSessionURLOrThrow(
|
||||||
workspaceId: string,
|
workspaceId: string,
|
||||||
returnUrlPath?: string,
|
returnUrlPath?: string,
|
||||||
) {
|
) {
|
||||||
const currentSubscriptionItem =
|
const currentSubscription =
|
||||||
await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow(
|
await this.billingSubscriptionService.getCurrentBillingSubscriptionOrThrow(
|
||||||
{
|
{
|
||||||
workspaceId,
|
workspaceId,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
const stripeCustomerId = currentSubscriptionItem.stripeCustomerId;
|
if (!currentSubscription) {
|
||||||
|
throw new Error('Error: missing subscription');
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripeCustomerId = currentSubscription.stripeCustomerId;
|
||||||
|
|
||||||
if (!stripeCustomerId) {
|
if (!stripeCustomerId) {
|
||||||
throw new Error('Error: missing stripeCustomerId');
|
throw new Error('Error: missing stripeCustomerId');
|
||||||
|
|||||||
Reference in New Issue
Block a user