2
0

🛂 Add new yearly plans and graduated pricing

BREAKING CHANGE: Stripe environment variables have changed. New ones are required. Check out the new Stripe configuration in the
docs.

Closes #457
This commit is contained in:
Baptiste Arnaud
2023-04-13 11:39:10 +02:00
parent 39d0dba18c
commit 2cbf8348c3
33 changed files with 1257 additions and 1399 deletions

View File

@ -33,7 +33,7 @@ export const Hero = () => {
bgClip="text"
data-aos="fade-up"
>
Open-source conversational forms builder
Build advanced chatbots visually
</Heading>
<Text
fontSize={['lg', 'xl']}

View File

@ -49,7 +49,7 @@ export const IntroducingChatApps = () => {
textAlign="center"
data-aos="fade"
>
Introducing Conversational Apps
Replace your old school forms with chatbots
</Heading>
<Text
textAlign="center"

View File

@ -0,0 +1,57 @@
import {
Stack,
Heading,
Button,
List,
ListItem,
ListIcon,
Text,
Link,
} from '@chakra-ui/react'
import { CheckCircleIcon } from 'assets/icons'
export const EnterprisePlanCard = () => (
<Stack
direction={['column', 'row']}
align="center"
p="10"
rounded="lg"
bgColor="gray.800"
borderWidth="2px"
spacing={10}
>
<Stack maxW="300px" spacing={4}>
<Heading fontSize="xl">Enterprise</Heading>
<Text>
Ideal for large companies looking to generate leads and automate
customer support at scale
</Text>
<Text fontSize="lg">
<Button
as={Link}
href="https://typebot.io/enterprise-lead-form"
isExternal
variant="outline"
>
Get a quote
</Button>
</Text>
</Stack>
<Stack flex="1">
<List spacing="4">
<ListItem fontWeight="medium" display="flex" alignItems="center">
<ListIcon fontSize="xl" as={CheckCircleIcon} marginEnd={2} />
Custom chats limits & seats for all your team
</ListItem>
<ListItem fontWeight="medium" display="flex" alignItems="center">
<ListIcon fontSize="xl" as={CheckCircleIcon} marginEnd={2} />
SSO & Granular access rights
</ListItem>
<ListItem fontWeight="medium" display="flex" alignItems="center">
<ListIcon fontSize="xl" as={CheckCircleIcon} marginEnd={2} />
Yearly contract with dedicated support representative
</ListItem>
</List>
</Stack>
</Stack>
)

View File

@ -0,0 +1,86 @@
import { Heading, VStack, SimpleGrid, Stack, Text } from '@chakra-ui/react'
export const Faq = () => (
<VStack w="full" spacing="10">
<Heading textAlign="center">Frequently asked questions</Heading>
<SimpleGrid columns={[1, 2]} spacing={10}>
<Stack borderWidth={1} p="8" rounded="lg" spacing={4}>
<Heading as="h2" fontSize="2xl">
What is considered a monthly chat?
</Heading>
<Text>
A chat is counted whenever a user starts a discussion. It is
independant of the number of messages he sends and receives. For
example if a user starts a discussion and sends 10 messages to the
bot, it will count as 1 chat. If the user chats again later and its
session is remembered, it will not be counted as a new chat. <br />
<br />
An easy way to think about it: 1 chat equals to a row in your Results
table
</Text>
</Stack>
<Stack borderWidth={1} p="8" rounded="lg" spacing={4}>
<Heading as="h2" fontSize="2xl">
What happens once I reach the monthly chats limit?
</Heading>
<Text>
When you exceed the number of chats included in your plan, you will
receive a heads up by email. There won&apos;t be any immediate
additional charges and your bots will continue to run. If you continue
to exceed the limit, you will be kindly asked you to upgrade your
subscription.
</Text>
</Stack>
<Stack borderWidth={1} p="8" rounded="lg" spacing={4}>
<Heading as="h2" fontSize="2xl">
What is considered as storage?
</Heading>
<Text>
You accumulate storage for every file that your user upload into your
bot. If you delete the associated result, it will free up the used
space.
</Text>
</Stack>
<Stack borderWidth={1} p="8" rounded="lg" spacing={4}>
<Heading as="h2" fontSize="2xl">
What happens once I reach the storage limit?
</Heading>
<Text>
When you exceed the storage size included in your plan, you will
receive a heads up by email. There won&apos;t be any immediate
additional charges and your bots will continue to store new files. If
you continue to exceed the limit, you will be kindly asked you to
upgrade your subscription.
</Text>
</Stack>
<Stack borderWidth={1} p="8" rounded="lg" spacing={4}>
<Heading as="h2" fontSize="2xl">
Can I cancel or change my subscription any time?
</Heading>
<Text>
Yes, you can cancel, upgrade or downgrade your subscription at any
time. There is no minimum time commitment or lock-in.
<br />
<br />
When you upgrade or downgrade your subscription, you&apos;ll get
access to the new options right away. Your next invoice will have a
prorated amount.
</Text>
</Stack>
<Stack borderWidth={1} p="8" rounded="lg" spacing={4}>
<Heading as="h2" fontSize="2xl">
Do you offer annual payments?
</Heading>
<Text>
Yes. Starter and Pro plans can be purchased with monthly or annual
billing.
<br />
<br />
Annual plans are cheaper and give you a 16% discount compared to
monthly payments. Enterprise plans are only available with annual
billing.
</Text>
</Stack>
</SimpleGrid>
</VStack>
)

View File

@ -3,6 +3,7 @@ import { HelpCircleIcon } from 'assets/icons/HelpCircleIcon'
import Link from 'next/link'
import React from 'react'
import { PricingCard } from './PricingCard'
import { chatsLimit } from '@typebot.io/lib/pricing'
export const FreePlanCard = () => (
<PricingCard
@ -13,7 +14,10 @@ export const FreePlanCard = () => (
'Unlimited typebots',
<>
<Text>
<chakra.span fontWeight="bold">300 chats</chakra.span> included
<chakra.span fontWeight="bold">
{chatsLimit.FREE.totalIncluded}
</chakra.span>{' '}
chats/month
</Text>
&nbsp;
<Tooltip
@ -37,7 +41,7 @@ export const FreePlanCard = () => (
as={Link}
href="https://app.typebot.io/register"
variant="outline"
colorScheme="blue"
colorScheme="gray"
size="lg"
w="full"
fontWeight="extrabold"

View File

@ -8,7 +8,6 @@ import {
Td,
Text,
Stack,
StackProps,
HStack,
Tooltip,
chakra,
@ -19,367 +18,383 @@ import { CheckIcon } from 'assets/icons/CheckIcon'
import { HelpCircleIcon } from 'assets/icons/HelpCircleIcon'
import { Plan } from '@typebot.io/prisma'
import Link from 'next/link'
import React, { useEffect, useState } from 'react'
import { chatsLimit, formatPrice, storageLimit } from '@typebot.io/lib/pricing'
import React from 'react'
import {
chatsLimit,
formatPrice,
prices,
seatsLimit,
storageLimit,
} from '@typebot.io/lib/pricing'
import { parseNumberWithCommas } from '@typebot.io/lib'
type Props = {
starterPrice: string
proPrice: string
} & StackProps
export const PlanComparisonTables = ({
starterPrice,
proPrice,
...props
}: Props) => {
const [additionalChatsPrice, setAdditionalChatsPrice] = useState(
`$${chatsLimit.STARTER.increaseStep.price}`
)
const [additionalStoragePrice, setAdditionalStoragePrice] = useState(
`$${storageLimit.STARTER.increaseStep.price}`
)
useEffect(() => {
setAdditionalChatsPrice(formatPrice(chatsLimit.STARTER.increaseStep.price))
setAdditionalStoragePrice(
formatPrice(storageLimit.STARTER.increaseStep.price)
)
}, [])
return (
<Stack spacing="12" {...props}>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th fontWeight="bold" color="white" w="400px">
Usage
</Th>
<Th>Free</Th>
<Th color="orange.200">Starter</Th>
<Th color="blue.200">Pro</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>Total bots</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
</Tr>
<Tr>
<Td>Chats</Td>
<Td>300 / month</Td>
<Td>2,000 / month</Td>
<Td>10,000 / month</Td>
</Tr>
<Tr>
<Td>Additional Chats</Td>
<Td />
<Td>{additionalChatsPrice} per 500</Td>
<Td>{additionalChatsPrice} per 1,000</Td>
</Tr>
<Tr>
<Td>Storage</Td>
<Td />
<Td>2 GB</Td>
<Td>10 GB</Td>
</Tr>
<Tr>
<Td>Additional Storage</Td>
<Td />
<Td>{additionalStoragePrice} per 1 GB</Td>
<Td>{additionalStoragePrice} per 1 GB</Td>
</Tr>
<Tr>
<Td>Members</Td>
<Td>Just you</Td>
<Td>2 seats</Td>
<Td>5 seats</Td>
</Tr>
<Tr>
<Td>Guests</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th fontWeight="bold" color="white" w="400px">
Features
</Th>
<Th>Free</Th>
<Th color="orange.200">Starter</Th>
<Th color="blue.200">Pro</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<TdWithTooltip
text="20+ blocks"
tooltip="Includes display bubbles (text, image, video, embed), question inputs (email, url, phone number...) and logic blocks (conditions, set variables...)"
/>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Starter templates</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Webhooks</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Google Sheets</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Google Analytics</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Send emails</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Zapier</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Pabbly Connect</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Make.com</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Custom Javascript & CSS</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Export CSV</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>File upload inputs</Td>
<Td />
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<TdWithTooltip
text="Folders"
tooltip="Organize your typebots into folders"
/>
<Td />
<Td>Unlimited</Td>
<Td>Unlimited</Td>
</Tr>
<Tr>
<Td>Remove branding</Td>
<Td />
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Custom domains</Td>
<Td />
<Td />
<Td>Unlimited</Td>
</Tr>
<Tr>
<TdWithTooltip
text="In-depth analytics"
tooltip="Analytics graph that shows your form drop-off rate, submission rate, and more."
/>
<Td />
<Td />
<Td>
<CheckIcon />
</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th fontWeight="bold" color="white" w="400px">
Support
</Th>
<Th>Free</Th>
<Th color="orange.200">Starter</Th>
<Th color="blue.200">Pro</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>Priority support</Td>
<Td />
<Td />
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Feature request priority</Td>
<Td />
<Td />
<Td>
<CheckIcon />
</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
<Stack
direction={['column', 'row']}
spacing={4}
w="full"
justify="space-around"
>
<Stack spacing={4}>
<Heading as="h3" size="md">
Personal
</Heading>
<Heading as="h3">Free</Heading>
<Link href="https://app.typebot.io/register">
<Button variant="outline">Get started</Button>
</Link>
</Stack>
<Stack spacing={4}>
<Heading as="h3" size="md" color="orange.200">
Starter
</Heading>
<Heading as="h3">
{starterPrice} <chakra.span fontSize="lg">/ month</chakra.span>
</Heading>
<Link
href={`https://app.typebot.io/register?subscribePlan=${Plan.STARTER}`}
>
<Button>Subscribe</Button>
</Link>
</Stack>
<Stack spacing={4}>
<Heading as="h3" size="md" color="blue.200">
Pro
</Heading>
<Heading as="h3">
{proPrice} <chakra.span fontSize="lg">/ month</chakra.span>
</Heading>
<Link
href={`https://app.typebot.io/register?subscribePlan=${Plan.PRO}`}
>
<Button>Subscribe</Button>
</Link>
</Stack>
export const PlanComparisonTables = () => (
<Stack spacing="12">
<TableContainer>
<Table>
<Thead>
<Tr>
<Th fontWeight="bold" color="white" w="400px">
Usage
</Th>
<Th>Free</Th>
<Th color="orange.200">Starter</Th>
<Th color="blue.200">Pro</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>Total bots</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
</Tr>
<Tr>
<Td>Chats</Td>
<Td>{chatsLimit.FREE.totalIncluded} / month</Td>
<Td>
{parseNumberWithCommas(
chatsLimit.STARTER.graduatedPrice[0].totalIncluded
)}{' '}
/ month
</Td>
<Td>
{parseNumberWithCommas(
chatsLimit.PRO.graduatedPrice[0].totalIncluded
)}{' '}
/ month
</Td>
</Tr>
<Tr>
<Td>Additional Chats</Td>
<Td />
<Td>
{formatPrice(chatsLimit.STARTER.graduatedPrice[1].price)} per{' '}
{chatsLimit.STARTER.graduatedPrice[1].totalIncluded -
chatsLimit.STARTER.graduatedPrice[0].totalIncluded}
</Td>
<Td>
{formatPrice(chatsLimit.PRO.graduatedPrice[1].price)} per{' '}
{chatsLimit.PRO.graduatedPrice[1].totalIncluded -
chatsLimit.PRO.graduatedPrice[0].totalIncluded}
</Td>
</Tr>
<Tr>
<Td>Storage</Td>
<Td />
<Td>2 GB</Td>
<Td>10 GB</Td>
</Tr>
<Tr>
<Td>Additional Storage</Td>
<Td />
<Td>
{formatPrice(storageLimit.STARTER.graduatedPrice[1].price)} per{' '}
{storageLimit.STARTER.graduatedPrice[1].totalIncluded -
storageLimit.STARTER.graduatedPrice[0].totalIncluded}{' '}
GB
</Td>
<Td>
{formatPrice(storageLimit.PRO.graduatedPrice[1].price)} per{' '}
{storageLimit.PRO.graduatedPrice[1].totalIncluded -
storageLimit.PRO.graduatedPrice[0].totalIncluded}{' '}
GB
</Td>
</Tr>
<Tr>
<Td>Members</Td>
<Td>Just you</Td>
<Td>{seatsLimit.STARTER.totalIncluded} seats</Td>
<Td>{seatsLimit.PRO.totalIncluded} seats</Td>
</Tr>
<Tr>
<Td>Guests</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
<Td>Unlimited</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th fontWeight="bold" color="white" w="400px">
Features
</Th>
<Th>Free</Th>
<Th color="orange.200">Starter</Th>
<Th color="blue.200">Pro</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<TdWithTooltip
text="20+ blocks"
tooltip="Includes display bubbles (text, image, video, embed), question inputs (email, url, phone number...) and logic blocks (conditions, set variables...)"
/>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Starter templates</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Webhooks</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Google Sheets</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Google Analytics</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Send emails</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Zapier</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Pabbly Connect</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Make.com</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Custom Javascript & CSS</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Export CSV</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>File upload inputs</Td>
<Td />
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<TdWithTooltip
text="Folders"
tooltip="Organize your typebots into folders"
/>
<Td />
<Td>Unlimited</Td>
<Td>Unlimited</Td>
</Tr>
<Tr>
<Td>Remove branding</Td>
<Td />
<Td>
<CheckIcon />
</Td>
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Custom domains</Td>
<Td />
<Td />
<Td>Unlimited</Td>
</Tr>
<Tr>
<TdWithTooltip
text="In-depth analytics"
tooltip="Analytics graph that shows your form drop-off rate, submission rate, and more."
/>
<Td />
<Td />
<Td>
<CheckIcon />
</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
<TableContainer>
<Table>
<Thead>
<Tr>
<Th fontWeight="bold" color="white" w="400px">
Support
</Th>
<Th>Free</Th>
<Th color="orange.200">Starter</Th>
<Th color="blue.200">Pro</Th>
</Tr>
</Thead>
<Tbody>
<Tr>
<Td>Priority support</Td>
<Td />
<Td />
<Td>
<CheckIcon />
</Td>
</Tr>
<Tr>
<Td>Feature request priority</Td>
<Td />
<Td />
<Td>
<CheckIcon />
</Td>
</Tr>
</Tbody>
</Table>
</TableContainer>
<Stack
direction={['column', 'row']}
spacing={4}
w="full"
justify="space-around"
>
<Stack spacing={4}>
<Heading as="h3" size="md">
Personal
</Heading>
<Heading as="h3">Free</Heading>
<Link href="https://app.typebot.io/register">
<Button variant="outline" colorScheme="gray">
Get started
</Button>
</Link>
</Stack>
<Stack spacing={4}>
<Heading as="h3" size="md" color="orange.200">
Starter
</Heading>
<Heading as="h3">
{formatPrice(prices.STARTER)}{' '}
<chakra.span fontSize="lg">/ month</chakra.span>
</Heading>
<Link
href={`https://app.typebot.io/register?subscribePlan=${Plan.STARTER}`}
>
<Button variant="outline" colorScheme="orange">
Subscribe
</Button>
</Link>
</Stack>
<Stack spacing={4}>
<Heading as="h3" size="md" color="blue.200">
Pro
</Heading>
<Heading as="h3">
{formatPrice(prices.PRO)}{' '}
<chakra.span fontSize="lg">/ month</chakra.span>
</Heading>
<Link
href={`https://app.typebot.io/register?subscribePlan=${Plan.PRO}`}
>
<Button>Subscribe</Button>
</Link>
</Stack>
</Stack>
)
}
</Stack>
)
const TdWithTooltip = ({
text,

View File

@ -9,7 +9,6 @@ import {
VStack,
} from '@chakra-ui/react'
import * as React from 'react'
import { useEffect, useState } from 'react'
import { formatPrice } from '@typebot.io/lib/pricing'
import { CheckCircleIcon } from '../../../assets/icons/CheckCircleIcon'
import { Card, CardProps } from './Card'
@ -34,12 +33,8 @@ export const PricingCard = ({
...rest
}: PricingCardProps) => {
const { features, price, name } = data
const [formattedPrice, setFormattedPrice] = useState(price)
const accentColor = useColorModeValue('blue.500', 'white')
useEffect(() => {
setFormattedPrice(typeof price === 'number' ? formatPrice(price) : price)
}, [price])
const formattedPrice = typeof price === 'number' ? formatPrice(price) : price
return (
<Card rounded="xl" bgColor="gray.800" {...rest}>

View File

@ -13,28 +13,33 @@ import { ChevronDownIcon } from 'assets/icons/ChevronDownIcon'
import { HelpCircleIcon } from 'assets/icons/HelpCircleIcon'
import { Plan } from '@typebot.io/prisma'
import Link from 'next/link'
import React, { useEffect, useState } from 'react'
import React, { useState } from 'react'
import { parseNumberWithCommas } from '@typebot.io/lib'
import { chatsLimit, computePrice, storageLimit } from '@typebot.io/lib/pricing'
import {
chatsLimit,
computePrice,
seatsLimit,
storageLimit,
} from '@typebot.io/lib/pricing'
import { PricingCard } from './PricingCard'
export const ProPlanCard = () => {
const [price, setPrice] = useState(89)
type Props = {
isYearly: boolean
}
export const ProPlanCard = ({ isYearly }: Props) => {
const [selectedChatsLimitIndex, setSelectedChatsLimitIndex] =
useState<number>(0)
const [selectedStorageLimitIndex, setSelectedStorageLimitIndex] =
useState<number>(0)
useEffect(() => {
setPrice(
computePrice(
Plan.PRO,
selectedChatsLimitIndex ?? 0,
selectedStorageLimitIndex ?? 0
) ?? NaN
)
}, [selectedChatsLimitIndex, selectedStorageLimitIndex])
const price =
computePrice(
Plan.PRO,
selectedChatsLimitIndex ?? 0,
selectedStorageLimitIndex ?? 0,
isYearly ? 'yearly' : 'monthly'
) ?? NaN
return (
<PricingCard
@ -44,7 +49,10 @@ export const ProPlanCard = () => {
featureLabel: 'Everything in Personal, plus:',
features: [
<Text key="seats">
<chakra.span fontWeight="bold">5 seats</chakra.span> included
<chakra.span fontWeight="bold">
{seatsLimit.PRO.totalIncluded} seats
</chakra.span>{' '}
included
</Text>,
<HStack key="chats" spacing={1.5}>
<Menu>
@ -57,50 +65,20 @@ export const ProPlanCard = () => {
>
{selectedChatsLimitIndex !== undefined
? parseNumberWithCommas(
chatsLimit.PRO.totalIncluded +
chatsLimit.PRO.increaseStep.amount *
selectedChatsLimitIndex
chatsLimit.PRO.graduatedPrice[selectedChatsLimitIndex]
.totalIncluded
)
: undefined}
</MenuButton>
<MenuList>
{selectedChatsLimitIndex !== 0 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(0)}>
{parseNumberWithCommas(chatsLimit.PRO.totalIncluded)}
{chatsLimit.PRO.graduatedPrice.map((price, index) => (
<MenuItem
key={index}
onClick={() => setSelectedChatsLimitIndex(index)}
>
{parseNumberWithCommas(price.totalIncluded)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 1 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(1)}>
{parseNumberWithCommas(
chatsLimit.PRO.totalIncluded +
chatsLimit.PRO.increaseStep.amount
)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 2 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(2)}>
{parseNumberWithCommas(
chatsLimit.PRO.totalIncluded +
chatsLimit.PRO.increaseStep.amount * 2
)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 3 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(3)}>
{parseNumberWithCommas(
chatsLimit.PRO.totalIncluded +
chatsLimit.PRO.increaseStep.amount * 3
)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 4 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(4)}>
{parseNumberWithCommas(
chatsLimit.PRO.totalIncluded +
chatsLimit.PRO.increaseStep.amount * 4
)}
</MenuItem>
)}
))}
</MenuList>
</Menu>{' '}
<Text>chats/mo</Text>
@ -126,50 +104,20 @@ export const ProPlanCard = () => {
>
{selectedStorageLimitIndex !== undefined
? parseNumberWithCommas(
storageLimit.PRO.totalIncluded +
storageLimit.PRO.increaseStep.amount *
selectedStorageLimitIndex
storageLimit.PRO.graduatedPrice[selectedStorageLimitIndex]
.totalIncluded
)
: undefined}
</MenuButton>
<MenuList>
{selectedStorageLimitIndex !== 0 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(0)}>
{parseNumberWithCommas(storageLimit.PRO.totalIncluded)}
{storageLimit.PRO.graduatedPrice.map((price, index) => (
<MenuItem
key={index}
onClick={() => setSelectedStorageLimitIndex(index)}
>
{parseNumberWithCommas(price.totalIncluded)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 1 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(1)}>
{parseNumberWithCommas(
storageLimit.PRO.totalIncluded +
storageLimit.PRO.increaseStep.amount
)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 2 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(2)}>
{parseNumberWithCommas(
storageLimit.PRO.totalIncluded +
storageLimit.PRO.increaseStep.amount * 2
)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 3 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(3)}>
{parseNumberWithCommas(
storageLimit.PRO.totalIncluded +
storageLimit.PRO.increaseStep.amount * 3
)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 4 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(4)}>
{parseNumberWithCommas(
storageLimit.PRO.totalIncluded +
storageLimit.PRO.increaseStep.amount * 4
)}
</MenuItem>
)}
))}
</MenuList>
</Menu>{' '}
<Text>GB of storage</Text>
@ -194,7 +142,7 @@ export const ProPlanCard = () => {
button={
<Button
as={Link}
href={`https://app.typebot.io/register?subscribePlan=${Plan.PRO}&chats=${selectedChatsLimitIndex}&storage=${selectedStorageLimitIndex}`}
href={`https://app.typebot.io/register?subscribePlan=${Plan.PRO}&chats=${selectedChatsLimitIndex}&storage=${selectedStorageLimitIndex}&isYearly=${isYearly}`}
colorScheme="blue"
size="lg"
w="full"

View File

@ -13,28 +13,32 @@ import { ChevronDownIcon } from 'assets/icons/ChevronDownIcon'
import { HelpCircleIcon } from 'assets/icons/HelpCircleIcon'
import { Plan } from '@typebot.io/prisma'
import Link from 'next/link'
import React, { useEffect, useState } from 'react'
import React, { useState } from 'react'
import { parseNumberWithCommas } from '@typebot.io/lib'
import { chatsLimit, computePrice, storageLimit } from '@typebot.io/lib/pricing'
import {
chatsLimit,
computePrice,
seatsLimit,
storageLimit,
} from '@typebot.io/lib/pricing'
import { PricingCard } from './PricingCard'
export const StarterPlanCard = () => {
const [price, setPrice] = useState(39)
type Props = {
isYearly: boolean
}
export const StarterPlanCard = ({ isYearly }: Props) => {
const [selectedChatsLimitIndex, setSelectedChatsLimitIndex] =
useState<number>(0)
const [selectedStorageLimitIndex, setSelectedStorageLimitIndex] =
useState<number>(0)
useEffect(() => {
setPrice(
computePrice(
Plan.STARTER,
selectedChatsLimitIndex ?? 0,
selectedStorageLimitIndex ?? 0
) ?? NaN
)
}, [selectedChatsLimitIndex, selectedStorageLimitIndex])
const price =
computePrice(
Plan.STARTER,
selectedChatsLimitIndex ?? 0,
selectedStorageLimitIndex ?? 0,
isYearly ? 'yearly' : 'monthly'
) ?? NaN
return (
<PricingCard
@ -44,7 +48,10 @@ export const StarterPlanCard = () => {
featureLabel: 'Everything in Personal, plus:',
features: [
<Text key="seats">
<chakra.span fontWeight="bold">2 seats</chakra.span> included
<chakra.span fontWeight="bold">
{seatsLimit.STARTER.totalIncluded} seats
</chakra.span>{' '}
included
</Text>,
<HStack key="chats" spacing={1.5}>
<Menu>
@ -56,49 +63,19 @@ export const StarterPlanCard = () => {
colorScheme="orange"
>
{parseNumberWithCommas(
chatsLimit.STARTER.totalIncluded +
chatsLimit.STARTER.increaseStep.amount *
selectedChatsLimitIndex
chatsLimit.STARTER.graduatedPrice[selectedChatsLimitIndex]
.totalIncluded
)}
</MenuButton>
<MenuList>
{selectedChatsLimitIndex !== 0 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(0)}>
{parseNumberWithCommas(chatsLimit.STARTER.totalIncluded)}
{chatsLimit.STARTER.graduatedPrice.map((price, index) => (
<MenuItem
key={index}
onClick={() => setSelectedChatsLimitIndex(index)}
>
{parseNumberWithCommas(price.totalIncluded)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 1 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(1)}>
{parseNumberWithCommas(
chatsLimit.STARTER.totalIncluded +
chatsLimit.STARTER.increaseStep.amount
)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 2 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(2)}>
{parseNumberWithCommas(
chatsLimit.STARTER.totalIncluded +
chatsLimit.STARTER.increaseStep.amount * 2
)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 3 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(3)}>
{parseNumberWithCommas(
chatsLimit.STARTER.totalIncluded +
chatsLimit.STARTER.increaseStep.amount * 3
)}
</MenuItem>
)}
{selectedChatsLimitIndex !== 4 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(4)}>
{parseNumberWithCommas(
chatsLimit.STARTER.totalIncluded +
chatsLimit.STARTER.increaseStep.amount * 4
)}
</MenuItem>
)}
))}
</MenuList>
</Menu>{' '}
<Text>chats/mo</Text>
@ -125,50 +102,21 @@ export const StarterPlanCard = () => {
>
{selectedStorageLimitIndex !== undefined
? parseNumberWithCommas(
storageLimit.STARTER.totalIncluded +
storageLimit.STARTER.increaseStep.amount *
selectedStorageLimitIndex
storageLimit.STARTER.graduatedPrice[
selectedStorageLimitIndex
].totalIncluded
)
: undefined}
</MenuButton>
<MenuList>
{selectedStorageLimitIndex !== 0 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(0)}>
{parseNumberWithCommas(storageLimit.STARTER.totalIncluded)}
{storageLimit.STARTER.graduatedPrice.map((price, index) => (
<MenuItem
key={index}
onClick={() => setSelectedStorageLimitIndex(index)}
>
{parseNumberWithCommas(price.totalIncluded)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 1 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(1)}>
{parseNumberWithCommas(
storageLimit.STARTER.totalIncluded +
storageLimit.STARTER.increaseStep.amount
)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 2 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(2)}>
{parseNumberWithCommas(
storageLimit.STARTER.totalIncluded +
storageLimit.STARTER.increaseStep.amount * 2
)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 3 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(3)}>
{parseNumberWithCommas(
storageLimit.STARTER.totalIncluded +
storageLimit.STARTER.increaseStep.amount * 3
)}
</MenuItem>
)}
{selectedStorageLimitIndex !== 4 && (
<MenuItem onClick={() => setSelectedStorageLimitIndex(4)}>
{parseNumberWithCommas(
storageLimit.STARTER.totalIncluded +
storageLimit.STARTER.increaseStep.amount * 4
)}
</MenuItem>
)}
))}
</MenuList>
</Menu>{' '}
<Text>GB of storage</Text>
@ -194,12 +142,13 @@ export const StarterPlanCard = () => {
button={
<Button
as={Link}
href={`https://app.typebot.io/register?subscribePlan=${Plan.STARTER}&chats=${selectedChatsLimitIndex}&storage=${selectedStorageLimitIndex}`}
colorScheme="blue"
href={`https://app.typebot.io/register?subscribePlan=${Plan.STARTER}&chats=${selectedChatsLimitIndex}&storage=${selectedStorageLimitIndex}&isYearly=${isYearly}`}
colorScheme="orange"
size="lg"
w="full"
fontWeight="extrabold"
py={{ md: '8' }}
variant="outline"
>
Subscribe now
</Button>