From 4a7a629824555887f09351bc47b9f8149490ee8c Mon Sep 17 00:00:00 2001 From: martmull Date: Thu, 7 Mar 2024 17:22:58 +0100 Subject: [PATCH] 44 add blocking middleware payment failed (#4339) * Add info ui component * Add info in billing settings * Add billing middleware * Handle subscription canceled webhook event * Stop deleting billingSubscription when subscription canceled * Handle subscription unpaid recovery * Handle subscription canceled status * Fix test * Add test * Fix test chatSupport display * Fix design --- .../effect-components/PageChangeEffect.tsx | 17 ++- .../__test__/useOnboardingStatus.test.ts | 64 ++++++++++- .../modules/auth/utils/getOnboardingStatus.ts | 18 ++- .../components/ManageYourSubscription.tsx | 24 ---- .../ui/display/info/components/Info.tsx | 65 +++++++++++ .../components/__stories__/Info.stories.tsx | 45 ++++++++ .../modules/ui/layout/page/DefaultLayout.tsx | 8 +- .../src/pages/settings/SettingsBilling.tsx | 108 +++++++++++++++--- .../src/core/billing/billing.controller.ts | 7 +- .../src/core/billing/billing.service.ts | 37 +++++- .../src/core/billing/stripe/stripe.service.ts | 19 +++ 11 files changed, 354 insertions(+), 58 deletions(-) delete mode 100644 packages/twenty-front/src/modules/billing/components/ManageYourSubscription.tsx create mode 100644 packages/twenty-front/src/modules/ui/display/info/components/Info.tsx create mode 100644 packages/twenty-front/src/modules/ui/display/info/components/__stories__/Info.stories.tsx diff --git a/packages/twenty-front/src/effect-components/PageChangeEffect.tsx b/packages/twenty-front/src/effect-components/PageChangeEffect.tsx index b86a15c6d..54c052c7a 100644 --- a/packages/twenty-front/src/effect-components/PageChangeEffect.tsx +++ b/packages/twenty-front/src/effect-components/PageChangeEffect.tsx @@ -64,7 +64,8 @@ export const PageChangeEffect = () => { isMatchingOngoingUserCreationRoute || isMatchingLocation(AppPath.CreateWorkspace) || isMatchingLocation(AppPath.CreateProfile) || - isMatchingLocation(AppPath.PlanRequired); + isMatchingLocation(AppPath.PlanRequired) || + isMatchingLocation(AppPath.PlanRequiredSuccess); const navigateToSignUp = () => { enqueueSnackBar('workspace does not exist', { @@ -81,12 +82,20 @@ export const PageChangeEffect = () => { navigate(AppPath.SignIn); } else if ( onboardingStatus && - [OnboardingStatus.Canceled, OnboardingStatus.Incomplete].includes( - onboardingStatus, - ) && + onboardingStatus === OnboardingStatus.Incomplete && !isMatchingLocation(AppPath.PlanRequired) ) { navigate(AppPath.PlanRequired); + } else if ( + onboardingStatus && + [OnboardingStatus.Unpaid, OnboardingStatus.Canceled].includes( + onboardingStatus, + ) && + !isMatchingLocation(SettingsPath.Billing) + ) { + navigate( + `${AppPath.SettingsCatchAll.replace('/*', '')}/${SettingsPath.Billing}`, + ); } else if ( onboardingStatus === OnboardingStatus.OngoingWorkspaceActivation && !isMatchingLocation(AppPath.CreateWorkspace) && diff --git a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts index ab47fa3af..0758ed0d7 100644 --- a/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts +++ b/packages/twenty-front/src/modules/auth/hooks/__test__/useOnboardingStatus.test.ts @@ -104,7 +104,13 @@ describe('useOnboardingStatus', () => { ...currentWorkspace, subscriptionStatus: 'canceled', }); - setCurrentWorkspaceMember(currentWorkspaceMember); + setCurrentWorkspaceMember({ + ...currentWorkspaceMember, + name: { + firstName: 'John', + lastName: 'Doe', + }, + }); }); expect(result.current.onboardingStatus).toBe('canceled'); @@ -178,4 +184,60 @@ describe('useOnboardingStatus', () => { expect(result.current.onboardingStatus).toBe('completed'); }); + + it('should return "past_due"', async () => { + const { result } = renderHooks(); + const { + setTokenPair, + setBilling, + setCurrentWorkspace, + setCurrentWorkspaceMember, + } = result.current; + + act(() => { + setTokenPair(tokenPair); + setBilling(billing); + setCurrentWorkspace({ + ...currentWorkspace, + subscriptionStatus: 'past_due', + }); + setCurrentWorkspaceMember({ + ...currentWorkspaceMember, + name: { + firstName: 'John', + lastName: 'Doe', + }, + }); + }); + + expect(result.current.onboardingStatus).toBe('past_due'); + }); + + it('should return "unpaid"', async () => { + const { result } = renderHooks(); + const { + setTokenPair, + setBilling, + setCurrentWorkspace, + setCurrentWorkspaceMember, + } = result.current; + + act(() => { + setTokenPair(tokenPair); + setBilling(billing); + setCurrentWorkspace({ + ...currentWorkspace, + subscriptionStatus: 'unpaid', + }); + setCurrentWorkspaceMember({ + ...currentWorkspaceMember, + name: { + firstName: 'John', + lastName: 'Doe', + }, + }); + }); + + expect(result.current.onboardingStatus).toBe('unpaid'); + }); }); diff --git a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts index 4479e730e..0ed331041 100644 --- a/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts +++ b/packages/twenty-front/src/modules/auth/utils/getOnboardingStatus.ts @@ -4,6 +4,8 @@ import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember'; export enum OnboardingStatus { Incomplete = 'incomplete', Canceled = 'canceled', + Unpaid = 'unpaid', + PastDue = 'past_due', OngoingUserCreation = 'ongoing_user_creation', OngoingWorkspaceActivation = 'ongoing_workspace_activation', OngoingProfileCreation = 'ongoing_profile_creation', @@ -41,10 +43,6 @@ export const getOnboardingStatus = ({ return OnboardingStatus.Incomplete; } - if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'canceled') { - return OnboardingStatus.Canceled; - } - if (currentWorkspace.activationStatus !== 'active') { return OnboardingStatus.OngoingWorkspaceActivation; } @@ -56,5 +54,17 @@ export const getOnboardingStatus = ({ return OnboardingStatus.OngoingProfileCreation; } + if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'canceled') { + return OnboardingStatus.Canceled; + } + + if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'past_due') { + return OnboardingStatus.PastDue; + } + + if (isBillingEnabled && currentWorkspace.subscriptionStatus === 'unpaid') { + return OnboardingStatus.Unpaid; + } + return OnboardingStatus.Completed; }; diff --git a/packages/twenty-front/src/modules/billing/components/ManageYourSubscription.tsx b/packages/twenty-front/src/modules/billing/components/ManageYourSubscription.tsx deleted file mode 100644 index e95067d18..000000000 --- a/packages/twenty-front/src/modules/billing/components/ManageYourSubscription.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { IconCreditCard } from '@/ui/display/icon'; -import { Button } from '@/ui/input/button/components/Button'; -import { useBillingPortalSessionQuery } from '~/generated/graphql.tsx'; -export const ManageYourSubscription = () => { - const { data, loading } = useBillingPortalSessionQuery({ - variables: { - returnUrlPath: '/settings/billing', - }, - }); - const handleButtonClick = () => { - if (data) { - window.location.replace(data.billingPortalSession.url); - } - }; - return ( -