Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 17 additions & 107 deletions apps/studio/components/interfaces/Organization/NewOrg/NewOrgForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { Elements } from '@stripe/react-stripe-js'
import type { PaymentIntentResult, PaymentMethod, StripeElementsOptions } from '@stripe/stripe-js'
import { loadStripe } from '@stripe/stripe-js'
import _ from 'lodash'
import { groupBy } from 'lodash'
import { HelpCircle } from 'lucide-react'
import { useTheme } from 'next-themes'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { parseAsBoolean, parseAsString, useQueryStates } from 'nuqs'
import { useEffect, useMemo, useRef, useState } from 'react'
Expand All @@ -16,6 +15,10 @@ import { z } from 'zod'
import { LOCAL_STORAGE_KEYS } from 'common'
import { getStripeElementsAppearanceOptions } from 'components/interfaces/Billing/Payment/Payment.utils'
import { PaymentConfirmation } from 'components/interfaces/Billing/Payment/PaymentConfirmation'
import {
NewPaymentMethodElement,
type PaymentMethodElementRef,
} from 'components/interfaces/Billing/Payment/PaymentMethods/NewPaymentMethodElement'
import SpendCapModal from 'components/interfaces/Billing/SpendCapModal'
import { InlineLink } from 'components/ui/InlineLink'
import Panel from 'components/ui/Panel'
Expand All @@ -41,16 +44,9 @@ import {
SelectTrigger_Shadcn_,
SelectValue_Shadcn_,
Switch,
Tooltip,
TooltipContent,
TooltipTrigger,
} from 'ui'
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
import {
NewPaymentMethodElement,
type PaymentMethodElementRef,
} from '../../Billing/Payment/PaymentMethods/NewPaymentMethodElement'
import { UpgradeExistingOrganizationCallout } from './UpgradeExistingOrganizationCallout'

const ORG_KIND_TYPES = {
PERSONAL: 'Personal',
Expand Down Expand Up @@ -132,12 +128,9 @@ export const NewOrgForm = ({
// in onSubmit below, which isn't a critical functionality imo so am okay for now. But ideally perhaps this data can
// be computed on the API and returned in /profile or something (since this data is on the account level)
const projectsByOrg = useMemo(() => {
return _.groupBy(projects, 'organization_slug')
return groupBy(projects, 'organization_slug')
}, [projects])

const [isOrgCreationConfirmationModalVisible, setIsOrgCreationConfirmationModalVisible] =
useState(false)

const stripeOptionsPaymentMethod: StripeElementsOptions = useMemo(
() =>
({
Expand Down Expand Up @@ -198,6 +191,11 @@ export const NewOrgForm = ({
const [showSpendCapHelperModal, setShowSpendCapHelperModal] = useState(false)
const [paymentIntentSecret, setPaymentIntentSecret] = useState<string | null>(null)

const hasFreeOrgWithProjects = useMemo(
() => freeOrgs.some((it) => projectsByOrg[it.slug]?.length > 0),
[freeOrgs, projectsByOrg]
)

const { mutate: createOrganization } = useOrganizationCreateMutation({
onSuccess: async (org) => {
if ('pending_payment_intent_secret' in org && org.pending_payment_intent_secret) {
Expand Down Expand Up @@ -299,13 +297,6 @@ export const NewOrgForm = ({
const paymentRef = useRef<PaymentMethodElementRef | null>(null)

const onSubmit: SubmitHandler<z.infer<typeof formSchema>> = async (formValues) => {
const hasFreeOrgWithProjects = freeOrgs.some((it) => projectsByOrg[it.slug]?.length > 0)

if (hasFreeOrgWithProjects && formValues.plan !== 'FREE') {
setIsOrgCreationConfirmationModalVisible(true)
return
}

setNewOrgLoading(true)

if (formValues.plan === 'FREE') {
Expand Down Expand Up @@ -478,15 +469,7 @@ export const NewOrgForm = ({
description={
<>
Which plan fits your organization's needs best?{' '}
<InlineLink
href="https://supabase.com/pricing"
target="_blank"
rel="noreferrer"
className="text-inherit hover:text-foreground transition-colors"
>
Learn more
</InlineLink>
.
<InlineLink href="https://supabase.com/pricing">Learn more</InlineLink>.
</>
}
>
Expand Down Expand Up @@ -569,86 +552,13 @@ export const NewOrgForm = ({
</Elements>
</Panel.Content>
)}

{hasFreeOrgWithProjects && form.getValues('plan') !== 'FREE' && (
<UpgradeExistingOrganizationCallout />
)}
</div>
</Panel>

<ConfirmationModal
size="large"
loading={newOrgLoading}
visible={isOrgCreationConfirmationModalVisible}
title="Confirm organization creation"
confirmLabel="Create new organization"
onCancel={() => setIsOrgCreationConfirmationModalVisible(false)}
onConfirm={async () => {
await onSubmit(form.getValues())
setIsOrgCreationConfirmationModalVisible(false)
}}
variant={'warning'}
>
<p className="text-sm text-foreground-light">
Supabase{' '}
<InlineLink
href="https://supabase.com/docs/guides/platform/billing-on-supabase"
target="_blank"
rel="noreferrer"
className="text-inherit hover:text-foreground transition-colors"
>
bills per organization
</InlineLink>
. If you want to upgrade your existing projects, upgrade your existing organization
instead.
</p>

<ul className="mt-4 divide-y divide-border-muted border-t border-t-border-muted">
{freeOrgs
.filter((it) => projectsByOrg[it.slug]?.length > 0)
.map((org) => {
const orgProjects = projectsByOrg[org.slug].map((it) => it.name)

return (
<li
key={`org_${org.slug}`}
className="pt-3 [&:not(:last-child)]:pb-3 flex items-center justify-between"
>
<div className="flex flex-col">
<h3 className="text-sm">{org.name}</h3>

<div className="text-foreground-lighter text-xs">
{orgProjects.length <= 2 ? (
<span>{orgProjects.join(' and ')}</span>
) : (
<div>
{orgProjects.slice(0, 2).join(', ')} and{' '}
<Tooltip>
<TooltipTrigger asChild>
<span className="underline decoration-dotted">
{orgProjects.length - 2} other{' '}
{orgProjects.length === 3 ? 'project' : 'project'}
</span>
</TooltipTrigger>
<TooltipContent>
<ul className="list-disc list-inside">
{orgProjects.slice(2).map((project) => (
<li>{project}</li>
))}
</ul>
</TooltipContent>
</Tooltip>
</div>
)}
</div>
</div>
<Button asChild type="default" size="tiny">
<Link href={`/org/${org.slug}/billing?panel=subscriptionPlan`}>
Change plan
</Link>
</Button>
</li>
)
})}
</ul>
</ConfirmationModal>

{stripePromise && paymentIntentSecret && paymentMethod && (
<Elements stripe={stripePromise} options={stripeOptionsConfirm}>
<PaymentConfirmation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { InlineLink } from 'components/ui/InlineLink'
import Panel from 'components/ui/Panel'
import { Admonition } from 'ui-patterns'

export const UpgradeExistingOrganizationCallout = () => {
return (
<Panel.Content>
<Admonition
type="default"
title="Looking to upgrade an existing project?"
description={
<div>
<p className="text-sm text-foreground-light">
Supabase{' '}
<InlineLink href="https://supabase.com/docs/guides/platform/billing-on-supabase">
bills per organization
</InlineLink>
. If you want to upgrade your existing projects,{' '}
<InlineLink href="/org/_/billing?panel=subscriptionPlan">
upgrade your existing organization
</InlineLink>{' '}
instead.
</p>
</div>
}
/>
</Panel.Content>
)
}
8 changes: 1 addition & 7 deletions apps/studio/pages/new/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import {
ProjectCreateVariables,
useProjectCreateMutation,
} from 'data/projects/project-create-mutation'
import { useTrack } from 'lib/telemetry/track'
import { useCustomContent } from 'hooks/custom-content/useCustomContent'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
Expand All @@ -63,6 +62,7 @@ import {
} from 'lib/constants'
import passwordStrength from 'lib/password-strength'
import { generateStrongPassword } from 'lib/project'
import { useTrack } from 'lib/telemetry/track'
import { AWS_REGIONS, type CloudProvider } from 'shared-data'
import type { NextPageWithLayout } from 'types'
import {
Expand Down Expand Up @@ -772,18 +772,12 @@ const Wizard: NextPageWithLayout = () => {
The size for your dedicated database. You can change this later.
Learn more about{' '}
<InlineLink
target="_blank"
rel="noopener noreferrer"
className="text-inherit hover:text-foreground transition-colors"
href={`${DOCS_URL}/guides/platform/compute-add-ons`}
>
compute add-ons
</InlineLink>{' '}
and{' '}
<InlineLink
target="_blank"
rel="noopener noreferrer"
className="text-inherit hover:text-foreground transition-colors"
href={`${DOCS_URL}/guides/platform/manage-your-usage/compute`}
>
compute billing
Expand Down
Loading