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:
Etienne
2025-01-16 11:10:36 +01:00
committed by GitHub
parent c79cb14132
commit 26058f3e25
40 changed files with 722 additions and 596 deletions

View File

@ -17,12 +17,14 @@ export const InformationBanner = ({
buttonTitle,
buttonIcon,
buttonOnClick,
isButtonDisabled = false,
}: {
message: string;
variant?: BannerVariant;
buttonTitle?: string;
buttonIcon?: IconComponent;
buttonOnClick?: () => void;
isButtonDisabled?: boolean;
}) => {
return (
<StyledBanner variant={variant}>
@ -35,6 +37,7 @@ export const InformationBanner = ({
size="small"
inverted
onClick={buttonOnClick}
disabled={isButtonDisabled}
/>
)}
</StyledBanner>

View File

@ -1,6 +1,13 @@
import { InformationBannerBillingSubscriptionPaused } from '@/information-banner/components/billing/InformationBannerBillingSubscriptionPaused';
import { InformationBannerFailPaymentInfo } from '@/information-banner/components/billing/InformationBannerFailPaymentInfo';
import { InformationBannerNoBillingSubscription } from '@/information-banner/components/billing/InformationBannerNoBillingSubscription';
import { InformationBannerReconnectAccountEmailAliases } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountEmailAliases';
import { InformationBannerReconnectAccountInsufficientPermissions } from '@/information-banner/components/reconnect-account/InformationBannerReconnectAccountInsufficientPermissions';
import { useIsWorkspaceActivationStatusSuspended } from '@/workspace/hooks/useIsWorkspaceActivationStatusSuspended';
import { useSubscriptionStatus } from '@/workspace/hooks/useSubscriptionStatus';
import styled from '@emotion/styled';
import { isDefined } from 'twenty-ui';
import { SubscriptionStatus } from '~/generated-metadata/graphql';
const StyledInformationBannerWrapper = styled.div`
height: 40px;
@ -12,10 +19,30 @@ const StyledInformationBannerWrapper = styled.div`
`;
export const InformationBannerWrapper = () => {
const subscriptionStatus = useSubscriptionStatus();
const isWorkspaceSuspended = useIsWorkspaceActivationStatusSuspended();
const displayBillingSubscriptionPausedBanner =
isWorkspaceSuspended && subscriptionStatus === SubscriptionStatus.Paused;
const displayBillingSubscriptionCanceledBanner =
isWorkspaceSuspended && !isDefined(subscriptionStatus);
const displayFailPaymentInfoBanner =
subscriptionStatus === SubscriptionStatus.PastDue ||
subscriptionStatus === SubscriptionStatus.Unpaid;
return (
<StyledInformationBannerWrapper>
<InformationBannerReconnectAccountInsufficientPermissions />
<InformationBannerReconnectAccountEmailAliases />
{displayBillingSubscriptionPausedBanner && (
<InformationBannerBillingSubscriptionPaused />
)}
{displayBillingSubscriptionCanceledBanner && (
<InformationBannerNoBillingSubscription />
)}
{displayFailPaymentInfoBanner && <InformationBannerFailPaymentInfo />}
</StyledInformationBannerWrapper>
);
};

View File

@ -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)}
/>
);
};

View File

@ -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)}
/>
);
};

View File

@ -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}
/>
);
};