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:
@ -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] };
|
||||||
@ -2386,7 +2386,7 @@ export type ValidatePasswordResetTokenQuery = { __typename?: 'Query', validatePa
|
|||||||
export type BillingBaseProductPricesQueryVariables = Exact<{ [key: string]: never; }>;
|
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<{
|
export type BillingPortalSessionQueryVariables = Exact<{
|
||||||
returnUrlPath?: InputMaybe<Scalars['String']>;
|
returnUrlPath?: InputMaybe<Scalars['String']>;
|
||||||
@ -3859,6 +3859,7 @@ export const BillingBaseProductPricesDocument = gql`
|
|||||||
plans {
|
plans {
|
||||||
planKey
|
planKey
|
||||||
baseProduct {
|
baseProduct {
|
||||||
|
name
|
||||||
prices {
|
prices {
|
||||||
... on BillingPriceLicensedDTO {
|
... on BillingPriceLicensedDTO {
|
||||||
unitAmount
|
unitAmount
|
||||||
|
|||||||
@ -5,6 +5,7 @@ export const BILLING_BASE_PRODUCT_PRICES = gql`
|
|||||||
plans {
|
plans {
|
||||||
planKey
|
planKey
|
||||||
baseProduct {
|
baseProduct {
|
||||||
|
name
|
||||||
prices {
|
prices {
|
||||||
... on BillingPriceLicensedDTO {
|
... on BillingPriceLicensedDTO {
|
||||||
unitAmount
|
unitAmount
|
||||||
|
|||||||
@ -88,20 +88,43 @@ export const ChooseYourPlan = () => {
|
|||||||
const billing = useRecoilValue(billingState);
|
const billing = useRecoilValue(billingState);
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
|
|
||||||
const benefits = [
|
const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState(
|
||||||
t`Full access`,
|
billingCheckoutSessionState,
|
||||||
t`Unlimited contacts`,
|
);
|
||||||
t`Email integration`,
|
|
||||||
t`Custom objects`,
|
|
||||||
t`API & Webhooks`,
|
|
||||||
t`1 000 workflow node executions`,
|
|
||||||
];
|
|
||||||
|
|
||||||
const { data: plans } = useBillingBaseProductPricesQuery();
|
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(
|
const baseProduct = plans?.plans.find(
|
||||||
(plan) => plan.planKey === BillingPlanKey.PRO,
|
(plan) => plan.planKey === currentPlan,
|
||||||
)?.baseProduct;
|
)?.baseProduct;
|
||||||
|
|
||||||
const baseProductPrice = baseProduct?.prices.find(
|
const baseProductPrice = baseProduct?.prices.find(
|
||||||
(price): price is BillingPriceLicensedDto =>
|
(price): price is BillingPriceLicensedDto =>
|
||||||
isBillingPriceLicensed(price) &&
|
isBillingPriceLicensed(price) &&
|
||||||
@ -116,10 +139,6 @@ export const ChooseYourPlan = () => {
|
|||||||
(trialPeriod) => trialPeriod.isCreditCardRequired,
|
(trialPeriod) => trialPeriod.isCreditCardRequired,
|
||||||
);
|
);
|
||||||
|
|
||||||
const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState(
|
|
||||||
billingCheckoutSessionState,
|
|
||||||
);
|
|
||||||
|
|
||||||
const { handleCheckoutSession, isSubmitting } = useHandleCheckoutSession({
|
const { handleCheckoutSession, isSubmitting } = useHandleCheckoutSession({
|
||||||
recurringInterval: billingCheckoutSession.interval,
|
recurringInterval: billingCheckoutSession.interval,
|
||||||
plan: billingCheckoutSession.plan,
|
plan: billingCheckoutSession.plan,
|
||||||
@ -133,7 +152,7 @@ export const ChooseYourPlan = () => {
|
|||||||
billingCheckoutSession.requirePaymentMethod !== withCreditCard
|
billingCheckoutSession.requirePaymentMethod !== withCreditCard
|
||||||
) {
|
) {
|
||||||
setBillingCheckoutSession({
|
setBillingCheckoutSession({
|
||||||
plan: billingCheckoutSession.plan,
|
plan: currentPlan,
|
||||||
interval: baseProductPrice.recurringInterval,
|
interval: baseProductPrice.recurringInterval,
|
||||||
requirePaymentMethod: withCreditCard,
|
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 { signOut } = useAuth();
|
||||||
|
|
||||||
const withCreditCardTrialPeriodDuration = withCreditCardTrialPeriod?.duration;
|
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 (
|
return (
|
||||||
isDefined(baseProductPrice) &&
|
isDefined(baseProductPrice) &&
|
||||||
isDefined(billing) && (
|
isDefined(billing) && (
|
||||||
@ -213,6 +253,10 @@ export const ChooseYourPlan = () => {
|
|||||||
<Trans>Log out</Trans>
|
<Trans>Log out</Trans>
|
||||||
</ActionLink>
|
</ActionLink>
|
||||||
<span />
|
<span />
|
||||||
|
<ActionLink onClick={handleSwitchPlan(alternatePlan)}>
|
||||||
|
<Trans>Switch to {alternatePlanName}</Trans>
|
||||||
|
</ActionLink>
|
||||||
|
<span />
|
||||||
<ActionLink href={CAL_LINK} target="_blank" rel="noreferrer">
|
<ActionLink href={CAL_LINK} target="_blank" rel="noreferrer">
|
||||||
<Trans>Book a Call</Trans>
|
<Trans>Book a Call</Trans>
|
||||||
</ActionLink>
|
</ActionLink>
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export class BillingPlanService {
|
|||||||
|
|
||||||
if (!baseProduct) {
|
if (!baseProduct) {
|
||||||
throw new BillingException(
|
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,
|
BillingExceptionCode.BILLING_PRODUCT_NOT_FOUND,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user