(billing) Implement custom plan

This commit is contained in:
Baptiste Arnaud
2022-10-27 11:32:21 +02:00
committed by Baptiste Arnaud
parent 3f7dc79918
commit 385853ca3c
23 changed files with 395 additions and 68 deletions

View File

@@ -1,4 +1,6 @@
import { Stack } from '@chakra-ui/react'
import { HStack, Stack, Text } from '@chakra-ui/react'
import { StripeClimateLogo } from 'assets/logos/StripeClimateLogo'
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
import { ChangePlanForm } from 'components/shared/ChangePlanForm'
import { useWorkspace } from 'contexts/WorkspaceContext'
import { Plan } from 'db'
@@ -26,7 +28,22 @@ export const BillingContent = () => {
})
}
/>
{workspace.plan !== Plan.LIFETIME &&
<HStack maxW="500px">
<StripeClimateLogo />
<Text fontSize="xs" color="gray.500">
Typebot is contributing 1% of your subscription to remove CO from
the atmosphere.{' '}
<NextChakraLink
href="https://climate.stripe.com/5VCRAq"
isExternal
textDecor="underline"
>
More info.
</NextChakraLink>
</Text>
</HStack>
{workspace.plan !== Plan.CUSTOM &&
workspace.plan !== Plan.LIFETIME &&
workspace.plan !== Plan.OFFERED && <ChangePlanForm />}
</Stack>

View File

@@ -49,7 +49,7 @@ export const AddMemberForm = ({
}
return (
<HStack as="form" onSubmit={handleInvitationSubmit} pb="4">
<HStack as="form" onSubmit={handleInvitationSubmit}>
<Input
placeholder="colleague@company.com"
name="inviteEmail"

View File

@@ -1,4 +1,10 @@
import { HStack, SkeletonCircle, SkeletonText, Stack } from '@chakra-ui/react'
import {
Heading,
HStack,
SkeletonCircle,
SkeletonText,
Stack,
} from '@chakra-ui/react'
import { UnlockPlanInfo } from 'components/shared/Info'
import { useUser } from 'contexts/UserContext'
import { useWorkspace } from 'contexts/WorkspaceContext'
@@ -12,6 +18,7 @@ import {
updateMember,
useMembers,
} from 'services/workspace'
import { getSeatsLimit } from 'utils'
import { AddMemberForm } from './AddMemberForm'
import { checkCanInviteMember } from './helpers'
import { MemberItem } from './MemberItem'
@@ -77,9 +84,12 @@ export const MembersList = () => {
})
}
const currentMembersCount = members.length + invitations.length
const canInviteNewMember = checkCanInviteMember({
plan: workspace?.plan,
currentMembersCount: [...members, ...invitations].length,
customSeatsLimit: workspace?.customSeatsLimit,
currentMembersCount,
})
return (
@@ -92,6 +102,11 @@ export const MembersList = () => {
`}
/>
)}
{workspace && (
<Heading fontSize="2xl">
Members ({currentMembersCount}/{getSeatsLimit(workspace)})
</Heading>
)}
{workspace?.id && canEdit && (
<AddMemberForm
workspaceId={workspace.id}

View File

@@ -1,14 +1,19 @@
import { Plan } from 'db'
import { seatsLimit } from 'utils'
import { getSeatsLimit } from 'utils'
export function checkCanInviteMember({
plan,
customSeatsLimit,
currentMembersCount,
}: {
plan?: Plan
customSeatsLimit?: number | null
currentMembersCount?: number
}) {
if (!plan || !currentMembersCount) return false
return seatsLimit[plan].totalIncluded > currentMembersCount
return (
getSeatsLimit({ plan, customSeatsLimit: customSeatsLimit ?? null }) >
currentMembersCount
)
}

View File

@@ -1,5 +1,4 @@
import { Stack, HStack, Text } from '@chakra-ui/react'
import { StripeClimateLogo } from 'assets/logos/StripeClimateLogo'
import { NextChakraLink } from 'components/nextChakra/NextChakraLink'
import { useUser } from 'contexts/UserContext'
import { useWorkspace } from 'contexts/WorkspaceContext'
@@ -65,20 +64,6 @@ export const ChangePlanForm = () => {
return (
<Stack spacing={6}>
<HStack maxW="500px">
<StripeClimateLogo />
<Text fontSize="xs" color="gray.500">
Typebot is contributing 1% of your subscription to remove CO from the
atmosphere.{' '}
<NextChakraLink
href="https://climate.stripe.com/5VCRAq"
isExternal
textDecor="underline"
>
More info.
</NextChakraLink>
</Text>
</HStack>
<HStack alignItems="stretch" spacing="4" w="full">
<StarterPlanContent
initialChatsLimitIndex={

View File

@@ -7,9 +7,13 @@ export const planColorSchemes: Record<Plan, ThemeTypings['colorSchemes']> = {
[Plan.OFFERED]: 'orange',
[Plan.STARTER]: 'orange',
[Plan.FREE]: 'gray',
[Plan.CUSTOM]: 'yellow',
}
export const PlanTag = ({ plan, ...props }: { plan?: Plan } & TagProps) => {
export const PlanTag = ({
plan,
...props
}: { plan: Plan } & TagProps): JSX.Element => {
switch (plan) {
case Plan.LIFETIME: {
return (
@@ -45,7 +49,7 @@ export const PlanTag = ({ plan, ...props }: { plan?: Plan } & TagProps) => {
</Tag>
)
}
default: {
case Plan.FREE: {
return (
<Tag
colorScheme={planColorSchemes[Plan.FREE]}
@@ -56,5 +60,16 @@ export const PlanTag = ({ plan, ...props }: { plan?: Plan } & TagProps) => {
</Tag>
)
}
case Plan.CUSTOM: {
return (
<Tag
colorScheme={planColorSchemes[Plan.CUSTOM]}
data-testid="free-plan-tag"
{...props}
>
Custom
</Tag>
)
}
}
}