Add enterprise plan in cloud onboarding (#11100)

Adding a way to switch to enterprise plan during onboarding

<img width="409" alt="Screenshot 2025-03-21 at 17 03 19"
src="https://github.com/user-attachments/assets/7a8c9ebd-3d77-4875-a141-c30fa5119eff"
/>
This commit is contained in:
Félix Malfait
2025-03-21 17:38:13 +01:00
committed by GitHub
parent e624e8deee
commit 07bd2486ca
4 changed files with 63 additions and 17 deletions

View File

@ -1,5 +1,5 @@
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
import { gql } from '@apollo/client';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
@ -2386,7 +2386,7 @@ export type ValidatePasswordResetTokenQuery = { __typename?: 'Query', validatePa
export type BillingBaseProductPricesQueryVariables = Exact<{ [key: string]: never; }>;
export type BillingBaseProductPricesQuery = { __typename?: 'Query', plans: Array<{ __typename?: 'BillingPlanOutput', planKey: BillingPlanKey, baseProduct: { __typename?: 'BillingProductDTO', prices: Array<{ __typename?: 'BillingPriceLicensedDTO', unitAmount: number, stripePriceId: string, recurringInterval: SubscriptionInterval } | { __typename?: 'BillingPriceMeteredDTO' }> } }> };
export type BillingBaseProductPricesQuery = { __typename?: 'Query', plans: Array<{ __typename?: 'BillingPlanOutput', planKey: BillingPlanKey, baseProduct: { __typename?: 'BillingProductDTO', name: string, prices: Array<{ __typename?: 'BillingPriceLicensedDTO', unitAmount: number, stripePriceId: string, recurringInterval: SubscriptionInterval } | { __typename?: 'BillingPriceMeteredDTO' }> } }> };
export type BillingPortalSessionQueryVariables = Exact<{
returnUrlPath?: InputMaybe<Scalars['String']>;
@ -3859,6 +3859,7 @@ export const BillingBaseProductPricesDocument = gql`
plans {
planKey
baseProduct {
name
prices {
... on BillingPriceLicensedDTO {
unitAmount

View File

@ -5,6 +5,7 @@ export const BILLING_BASE_PRODUCT_PRICES = gql`
plans {
planKey
baseProduct {
name
prices {
... on BillingPriceLicensedDTO {
unitAmount

View File

@ -88,20 +88,43 @@ export const ChooseYourPlan = () => {
const billing = useRecoilValue(billingState);
const { t } = useLingui();
const benefits = [
t`Full access`,
t`Unlimited contacts`,
t`Email integration`,
t`Custom objects`,
t`API & Webhooks`,
t`1 000 workflow node executions`,
];
const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState(
billingCheckoutSessionState,
);
const { data: plans } = useBillingBaseProductPricesQuery();
const currentPlan = billingCheckoutSession.plan || BillingPlanKey.PRO;
const getPlanBenefits = (planKey: BillingPlanKey) => {
if (planKey === BillingPlanKey.ENTERPRISE) {
return [
t`Full access`,
t`Unlimited contacts`,
t`Email integration`,
t`Custom objects`,
t`API & Webhooks`,
t`20 000 workflow node executions`,
t`SSO (SAML / OIDC)`,
];
}
return [
t`Full access`,
t`Unlimited contacts`,
t`Email integration`,
t`Custom objects`,
t`API & Webhooks`,
t`10 000 workflow node executions`,
];
};
const benefits = getPlanBenefits(currentPlan);
const baseProduct = plans?.plans.find(
(plan) => plan.planKey === BillingPlanKey.PRO,
(plan) => plan.planKey === currentPlan,
)?.baseProduct;
const baseProductPrice = baseProduct?.prices.find(
(price): price is BillingPriceLicensedDto =>
isBillingPriceLicensed(price) &&
@ -116,10 +139,6 @@ export const ChooseYourPlan = () => {
(trialPeriod) => trialPeriod.isCreditCardRequired,
);
const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState(
billingCheckoutSessionState,
);
const { handleCheckoutSession, isSubmitting } = useHandleCheckoutSession({
recurringInterval: billingCheckoutSession.interval,
plan: billingCheckoutSession.plan,
@ -133,7 +152,7 @@ export const ChooseYourPlan = () => {
billingCheckoutSession.requirePaymentMethod !== withCreditCard
) {
setBillingCheckoutSession({
plan: billingCheckoutSession.plan,
plan: currentPlan,
interval: baseProductPrice.recurringInterval,
requirePaymentMethod: withCreditCard,
});
@ -141,10 +160,31 @@ export const ChooseYourPlan = () => {
};
};
const handleSwitchPlan = (planKey: BillingPlanKey) => {
return () => {
if (isDefined(baseProductPrice)) {
setBillingCheckoutSession({
plan: planKey,
interval: baseProductPrice.recurringInterval,
requirePaymentMethod: billingCheckoutSession.requirePaymentMethod,
});
}
};
};
const { signOut } = useAuth();
const withCreditCardTrialPeriodDuration = withCreditCardTrialPeriod?.duration;
const alternatePlan =
currentPlan === BillingPlanKey.PRO
? BillingPlanKey.ENTERPRISE
: BillingPlanKey.PRO;
const alternatePlanName = plans?.plans.find(
(plan) => plan.planKey === alternatePlan,
)?.baseProduct.name;
return (
isDefined(baseProductPrice) &&
isDefined(billing) && (
@ -213,6 +253,10 @@ export const ChooseYourPlan = () => {
<Trans>Log out</Trans>
</ActionLink>
<span />
<ActionLink onClick={handleSwitchPlan(alternatePlan)}>
<Trans>Switch to {alternatePlanName}</Trans>
</ActionLink>
<span />
<ActionLink href={CAL_LINK} target="_blank" rel="noreferrer">
<Trans>Book a Call</Trans>
</ActionLink>

View File

@ -85,7 +85,7 @@ export class BillingPlanService {
if (!baseProduct) {
throw new BillingException(
'Base product not found, did you run the billing:sync-products command?',
'Base product not found, did you run the billing:sync-plans-data command?',
BillingExceptionCode.BILLING_PRODUCT_NOT_FOUND,
);
}