2
0
Files
bot/apps/builder/src/features/billing/components/ChangePlanForm/ProPlanContent.tsx
2023-03-15 08:35:16 +01:00

347 lines
12 KiB
TypeScript

import {
Stack,
Heading,
chakra,
HStack,
Menu,
MenuButton,
Button,
MenuList,
MenuItem,
Text,
Tooltip,
Flex,
Tag,
useColorModeValue,
} from '@chakra-ui/react'
import { ChevronLeftIcon } from '@/components/icons'
import { useWorkspace } from '@/features/workspace'
import { Plan } from '@typebot.io/prisma'
import { useEffect, useState } from 'react'
import { parseNumberWithCommas } from '@typebot.io/lib'
import {
chatsLimit,
computePrice,
formatPrice,
getChatsLimit,
getStorageLimit,
storageLimit,
} from '@typebot.io/lib/pricing'
import { FeaturesList } from './components/FeaturesList'
import { MoreInfoTooltip } from '@/components/MoreInfoTooltip'
type ProPlanContentProps = {
initialChatsLimitIndex?: number
initialStorageLimitIndex?: number
currency?: 'usd' | 'eur'
isLoading: boolean
onPayClick: (props: {
selectedChatsLimitIndex: number
selectedStorageLimitIndex: number
}) => void
}
export const ProPlanContent = ({
initialChatsLimitIndex,
initialStorageLimitIndex,
currency,
isLoading,
onPayClick,
}: ProPlanContentProps) => {
const { workspace } = useWorkspace()
const [selectedChatsLimitIndex, setSelectedChatsLimitIndex] =
useState<number>()
const [selectedStorageLimitIndex, setSelectedStorageLimitIndex] =
useState<number>()
useEffect(() => {
if (
selectedChatsLimitIndex === undefined &&
initialChatsLimitIndex !== undefined
)
setSelectedChatsLimitIndex(initialChatsLimitIndex)
if (
selectedStorageLimitIndex === undefined &&
initialStorageLimitIndex !== undefined
)
setSelectedStorageLimitIndex(initialStorageLimitIndex)
}, [
initialChatsLimitIndex,
initialStorageLimitIndex,
selectedChatsLimitIndex,
selectedStorageLimitIndex,
])
const workspaceChatsLimit = workspace ? getChatsLimit(workspace) : undefined
const workspaceStorageLimit = workspace
? getStorageLimit(workspace)
: undefined
const isCurrentPlan =
chatsLimit[Plan.PRO].totalIncluded +
chatsLimit[Plan.PRO].increaseStep.amount *
(selectedChatsLimitIndex ?? 0) ===
workspaceChatsLimit &&
storageLimit[Plan.PRO].totalIncluded +
storageLimit[Plan.PRO].increaseStep.amount *
(selectedStorageLimitIndex ?? 0) ===
workspaceStorageLimit
const getButtonLabel = () => {
if (
selectedChatsLimitIndex === undefined ||
selectedStorageLimitIndex === undefined
)
return ''
if (workspace?.plan === Plan.PRO) {
if (isCurrentPlan) return 'Your current plan'
if (
selectedChatsLimitIndex !== initialChatsLimitIndex ||
selectedStorageLimitIndex !== initialStorageLimitIndex
)
return 'Update'
}
return 'Upgrade'
}
const handlePayClick = async () => {
if (
selectedChatsLimitIndex === undefined ||
selectedStorageLimitIndex === undefined
)
return
onPayClick({
selectedChatsLimitIndex,
selectedStorageLimitIndex,
})
}
return (
<Flex
p="6"
pos="relative"
h="full"
flexDir="column"
flex="1"
flexShrink={0}
borderWidth="1px"
borderColor={useColorModeValue('blue.500', 'blue.300')}
rounded="lg"
>
<Flex justifyContent="center">
<Tag
pos="absolute"
top="-10px"
colorScheme="blue"
bg={useColorModeValue('blue.500', 'blue.400')}
variant="solid"
fontWeight="semibold"
style={{ marginTop: 0 }}
>
Most popular
</Tag>
</Flex>
<Stack justifyContent="space-between" h="full">
<Stack spacing="4" mt={2}>
<Heading fontSize="2xl">
Upgrade to <chakra.span color="blue.400">Pro</chakra.span>
</Heading>
<Text>For agencies & growing startups.</Text>
</Stack>
<Stack spacing="4">
<Heading>
{formatPrice(
computePrice(
Plan.PRO,
selectedChatsLimitIndex ?? 0,
selectedStorageLimitIndex ?? 0
) ?? NaN,
currency
)}
<chakra.span fontSize="md">/ month</chakra.span>
</Heading>
<Text fontWeight="bold">
<Tooltip
label={
<FeaturesList
features={[
'Branding removed',
'File upload input block',
'Create folders',
]}
spacing="0"
/>
}
hasArrow
placement="top"
>
<chakra.span textDecoration="underline" cursor="pointer">
Everything in Starter
</chakra.span>
</Tooltip>
, plus:
</Text>
<FeaturesList
features={[
'5 seats included',
<HStack key="test">
<Text>
<Menu>
<MenuButton
as={Button}
rightIcon={<ChevronLeftIcon transform="rotate(-90deg)" />}
size="sm"
isLoading={selectedChatsLimitIndex === undefined}
>
{selectedChatsLimitIndex !== undefined
? parseNumberWithCommas(
chatsLimit.PRO.totalIncluded +
chatsLimit.PRO.increaseStep.amount *
selectedChatsLimitIndex
)
: undefined}
</MenuButton>
<MenuList>
{selectedChatsLimitIndex !== 0 && (
<MenuItem onClick={() => setSelectedChatsLimitIndex(0)}>
{parseNumberWithCommas(chatsLimit.PRO.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>{' '}
chats/mo
</Text>
<MoreInfoTooltip>
A chat is counted whenever a user starts a discussion. It is
independant of the number of messages he sends and receives.
</MoreInfoTooltip>
</HStack>,
<HStack key="test">
<Text>
<Menu>
<MenuButton
as={Button}
rightIcon={<ChevronLeftIcon transform="rotate(-90deg)" />}
size="sm"
isLoading={selectedStorageLimitIndex === undefined}
>
{selectedStorageLimitIndex !== undefined
? parseNumberWithCommas(
storageLimit.PRO.totalIncluded +
storageLimit.PRO.increaseStep.amount *
selectedStorageLimitIndex
)
: undefined}
</MenuButton>
<MenuList>
{selectedStorageLimitIndex !== 0 && (
<MenuItem
onClick={() => setSelectedStorageLimitIndex(0)}
>
{parseNumberWithCommas(
storageLimit.PRO.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>{' '}
GB of storage
</Text>
<MoreInfoTooltip>
You accumulate storage for every file that your user upload
into your bot. If you delete the result, it will free up the
space.
</MoreInfoTooltip>
</HStack>,
'Custom domains',
'In-depth analytics',
]}
/>
<Button
colorScheme="blue"
variant="outline"
onClick={handlePayClick}
isLoading={isLoading}
isDisabled={isCurrentPlan}
>
{getButtonLabel()}
</Button>
</Stack>
</Stack>
</Flex>
)
}