feat: Pricing for TPA (#28454)

* Don't set fixed height on the generateSecret button in hooks sheet.

* Block two of the TPA providers on free plan orgs.

* Fix some copy. Add more explanation about the pricing.

* Update copy

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Ivan VasilovandJoshen Lim authored and GitHub committed 2024-08-14 11:01:58 +02:00
1 parent d4eb4ed341
commit b73317fc67
4 files changed
+94 -26

No files matched your search

@@ -225,7 +225,7 @@ export const CreateHookSheet = ({
return (
<Sheet open={visible} onOpenChange={() => onClose()}>
<SheetContent showClose={false} className="flex flex-col gap-0">
<SheetContent size="lg" showClose={false} className="flex flex-col gap-0">
<SheetHeader className="py-3 flex flex-row justify-between items-center border-b-0">
<div className="flex flex-row gap-3 items-center">
<SheetClose
@@ -383,7 +383,7 @@ export const CreateHookSheet = ({
<Button
type="default"
size="small"
className="rounded-l-none h-[38px]"
className="rounded-l-none"
onClick={() => {
const authHookSecret = generateAuthHookSecret()
form.setValue('httpsValues.secret', authHookSecret)
@@ -1,18 +1,25 @@
import { ChevronDown } from 'lucide-react'
import Image from 'next/image'
import { IS_PLATFORM } from 'common'
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
import {
Button,
cn,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from 'ui'
import {
INTEGRATION_TYPES,
getIntegrationTypeIcon,
getIntegrationTypeLabel,
INTEGRATION_TYPES,
} from './ThirdPartyAuthForm.utils'
import Link from 'next/link'
interface AddIntegrationDropdownProps {
buttonText?: string
@@ -21,9 +28,38 @@ interface AddIntegrationDropdownProps {
const Providers: INTEGRATION_TYPES[] = ['firebase', 'auth0', 'awsCognito']
const ProviderDropdownItem = ({
disabled,
type,
onSelectIntegrationType,
}: {
disabled?: boolean
type: INTEGRATION_TYPES
onSelectIntegrationType: (type: INTEGRATION_TYPES) => void
}) => {
return (
<DropdownMenuItem
key={type}
onClick={() => onSelectIntegrationType(type)}
className={cn('flex items-center gap-x-2 p-2', disabled && 'cursor-not-allowed')}
disabled={disabled}
>
<Image src={getIntegrationTypeIcon(type)} width={16} height={16} alt={`${type} icon`} />
<span>{getIntegrationTypeLabel(type)}</span>
</DropdownMenuItem>
)
}
export const AddIntegrationDropdown = ({
onSelectIntegrationType,
}: AddIntegrationDropdownProps) => {
const organization = useSelectedOrganization()
const { data: subscription } = useOrgSubscriptionQuery(
{ orgSlug: organization?.slug },
{ enabled: IS_PLATFORM }
)
return (
<DropdownMenu modal={false}>
<DropdownMenuTrigger asChild>
@@ -31,25 +67,48 @@ export const AddIntegrationDropdown = ({
Add provider
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{Providers.map((type) => {
const name = getIntegrationTypeLabel(type)
return (
<DropdownMenuItem
key={name}
onClick={() => onSelectIntegrationType(type)}
className="flex items-center gap-x-2 p-2"
>
<Image
src={getIntegrationTypeIcon(type)}
width={16}
height={16}
alt={`${name} icon`}
<DropdownMenuContent align="end" className="w-56">
<ProviderDropdownItem type="firebase" onSelectIntegrationType={onSelectIntegrationType} />
{subscription?.plan.id === 'free' ? (
<>
<DropdownMenuSeparator />
<div className="bg-surface-200 -m-1 p-2">
<DropdownMenuLabel className="grid gap-1">
<p className="text-foreground-light">Unavailable on the Free plan</p>
<p className="text-foreground-lighter text-xs">
<Link
target="_href"
rel="noreferrer"
className="underline hover:text-foreground-light transition"
href={`/org/${organization?.slug}/billing`}
>
Upgrade your plan
</Link>{' '}
to add the following providers to your project.
</p>
</DropdownMenuLabel>
<ProviderDropdownItem
disabled
type="auth0"
onSelectIntegrationType={onSelectIntegrationType}
/>
<span>{name}</span>
</DropdownMenuItem>
)
})}
<ProviderDropdownItem
disabled
type="awsCognito"
onSelectIntegrationType={onSelectIntegrationType}
/>
</div>
</>
) : (
<>
<ProviderDropdownItem type="auth0" onSelectIntegrationType={onSelectIntegrationType} />
<ProviderDropdownItem
type="awsCognito"
onSelectIntegrationType={onSelectIntegrationType}
/>
</>
)}
</DropdownMenuContent>
</DropdownMenu>
)
@@ -21,9 +21,9 @@ import { CreateAwsCognitoAuthIntegrationDialog } from './CreateAwsCognitoAuthDia
import { CreateFirebaseAuthIntegrationDialog } from './CreateFirebaseAuthDialog'
import { IntegrationCard } from './IntegrationCard'
import {
INTEGRATION_TYPES,
getIntegrationType,
getIntegrationTypeLabel,
INTEGRATION_TYPES,
} from './ThirdPartyAuthForm.utils'
export const ThirdPartyAuthForm = () => {
@@ -62,14 +62,23 @@ export const ThirdPartyAuthForm = () => {
<div className="pb-4">
<FormHeader
title="Third Party Auth"
className="mb-1"
description="Use third-party authentication (TPA) systems based on JWTs to access your project."
actions={
integrations.length !== 0 ? (
<AddIntegrationDropdown onSelectIntegrationType={setSelectedIntegration} />
) : null
}
docsUrl="https://supabase.com/docs/guides/auth/third-party/overview"
/>
<div className="prose text-sm mb-6 max-w-full">
<span>
Billing is based on the number of monthly active users (MAUs) requesting your API
throughout the billing period (50 included then you'll be charged{' '}
</span>
<span className="text-brand">$0.00325</span>
<span> per MAU).</span>
</div>
{isLoading && (
<div
className={cn(
@@ -87,7 +96,7 @@ export const ThirdPartyAuthForm = () => {
'border rounded border-default px-20 py-16 flex flex-col items-center justify-center space-y-4'
)}
>
<p className="text-sm text-foreground-light">No integrations configured yet</p>
<p className="text-sm text-foreground-light">No providers configured yet</p>
<AddIntegrationDropdown
buttonText="Add a new integration"
onSelectIntegrationType={setSelectedIntegration}
@@ -3,8 +3,8 @@ import Link from 'next/link'
import ReactMarkdown from 'react-markdown'
import { Markdown } from 'components/interfaces/Markdown'
import { Button } from 'ui'
import { ReactNode } from 'react'
import { Button, cn } from 'ui'
const FormHeader = ({
title,
@@ -20,7 +20,7 @@ const FormHeader = ({
className?: string
}) => {
return (
<div className={`mb-6 flex items-center justify-between ${className}`}>
<div className={cn(`mb-6 flex items-center justify-between ${className}`)}>
<div className="space-y-1">
<h3 className="text-foreground text-xl">
<ReactMarkdown unwrapDisallowed disallowedElements={['p']}>