import { SubTitle } from '@/auth/components/SubTitle'; import { Title } from '@/auth/components/Title'; import { useAuth } from '@/auth/hooks/useAuth'; import { billingCheckoutSessionState } from '@/auth/states/billingCheckoutSessionState'; import { SubscriptionBenefit } from '@/billing/components/SubscriptionBenefit'; import { SubscriptionCard } from '@/billing/components/SubscriptionCard'; import { billingState } from '@/client-config/states/billingState'; import { AppPath } from '@/types/AppPath'; import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import styled from '@emotion/styled'; import { isNonEmptyString, isNumber } from '@sniptt/guards'; import { useState } from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { ActionLink, CAL_LINK, CardPicker, Loader, MainButton, } from 'twenty-ui'; import { ProductPriceEntity, SubscriptionInterval, useCheckoutSessionMutation, useGetProductPricesQuery, } from '~/generated/graphql'; import { isDefined } from '~/utils/isDefined'; const StyledChoosePlanContainer = styled.div` display: flex; flex-direction: row; width: 100%; margin: ${({ theme }) => theme.spacing(8)} 0 ${({ theme }) => theme.spacing(2)}; gap: ${({ theme }) => theme.spacing(2)}; `; const StyledBenefitsContainer = styled.div` background-color: ${({ theme }) => theme.background.secondary}; border: 1px solid ${({ theme }) => theme.border.color.medium}; border-radius: ${({ theme }) => theme.border.radius.md}; box-sizing: border-box; display: flex; flex-direction: column; width: 100%; gap: 16px; padding: ${({ theme }) => theme.spacing(4)} ${({ theme }) => theme.spacing(3)}; margin-bottom: ${({ theme }) => theme.spacing(8)}; `; const StyledLinkGroup = styled.div` align-items: center; display: flex; flex-direction: row; gap: ${({ theme }) => theme.spacing(1)}; justify-content: center; margin-top: ${({ theme }) => theme.spacing(4)}; > span { background-color: ${({ theme }) => theme.font.color.light}; border-radius: 50%; height: 2px; width: 2px; } `; const benefits = [ 'Full access', 'Unlimited contacts', 'Email integration', 'Custom objects', 'API & Webhooks', 'Frequent updates', 'And much more', ]; export const ChooseYourPlan = () => { const billing = useRecoilValue(billingState); const [isSubmitting, setIsSubmitting] = useState(false); const { enqueueSnackBar } = useSnackBar(); const { data: prices } = useGetProductPricesQuery({ variables: { product: 'base-plan' }, }); const [billingCheckoutSession, setBillingCheckoutSession] = useRecoilState( billingCheckoutSessionState, ); const [checkoutSession] = useCheckoutSessionMutation(); const handleCheckoutSession = async () => { setIsSubmitting(true); const { data } = await checkoutSession({ variables: { recurringInterval: billingCheckoutSession.interval, successUrlPath: AppPath.PlanRequiredSuccess, plan: billingCheckoutSession.plan, requirePaymentMethod: billingCheckoutSession.requirePaymentMethod, }, }); setIsSubmitting(false); if (!data?.checkoutSession.url) { enqueueSnackBar( 'Checkout session error. Please retry or contact Twenty team', { variant: SnackBarVariant.Error, }, ); return; } window.location.replace(data.checkoutSession.url); }; const handleIntervalChange = (type?: SubscriptionInterval) => { return () => { if (isNonEmptyString(type) && billingCheckoutSession.interval !== type) { setBillingCheckoutSession({ plan: billingCheckoutSession.plan, interval: type, requirePaymentMethod: billingCheckoutSession.requirePaymentMethod, skipPlanPage: false, }); } }; }; const { signOut } = useAuth(); const computeInfo = ( price: ProductPriceEntity, prices: ProductPriceEntity[], ): string => { if (price.recurringInterval !== SubscriptionInterval.Year) { return 'Cancel anytime'; } const monthPrice = prices.filter( (price) => price.recurringInterval === SubscriptionInterval.Month, )?.[0]; if ( isDefined(monthPrice) && isNumber(monthPrice.unitAmount) && monthPrice.unitAmount > 0 && isNumber(price.unitAmount) && price.unitAmount > 0 ) { return `Save $${(12 * monthPrice.unitAmount - price.unitAmount) / 100}`; } return 'Cancel anytime'; }; if (billingCheckoutSession.skipPlanPage && !isSubmitting) { handleCheckoutSession(); } if (billingCheckoutSession.skipPlanPage || isSubmitting) { return ; } return ( prices?.getProductPrices?.productPrices && ( <> Choose your Plan Enjoy a {billing?.billingFreeTrialDurationInDays}-day free trial {prices.getProductPrices.productPrices.map((price, index) => ( ))} {benefits.map((benefit, index) => ( {benefit} ))} isSubmitting && } disabled={isSubmitting} /> Log out Book a Call ) ); };