Files
supabase/apps/studio/components/ui/UpgradePlanButton.tsx
Danny White 6ac5bf6b86 feat(studio): point replica deep links at Infrastructure and recommend compute (#48921)
## What kind of change does this PR introduce?

Feature. Stack 5 of 5 (tip) for
[PIPE-1007](https://linear.app/supabase/issue/PIPE-1007/move-read-replicas-out-of-replication-into-infrastructure).

## What is the current behavior?

Selectors still open Replication with `destinationType=Read+Replica`.
Compute eligibility actions leave the add-replica flow without carrying
a recommended size into the Infrastructure form.

## What is the new behavior?

DatabaseSelector and the SQL submenu open Infrastructure
`?addReplica=true`. Change to Small/XL compute waits for the sheet to
close, pre-selects that size, then focuses and scrolls the
Infrastructure form to Compute without shifting the page footer.

## Additional context

Last stack PR. [#49043](https://github.com/supabase/supabase/pull/49043)
has merged. Please review, but do not merge until 2→4 are also approved.
Then merge [#49044](https://github.com/supabase/supabase/pull/49044) →
[#49045](https://github.com/supabase/supabase/pull/49045) →
[#49046](https://github.com/supabase/supabase/pull/49046) → this PR in
succession, and drop the `do-not-merge` labels.

Update the read replicas getting-started doc in the same sitting so it
points only at Infrastructure (it currently also links Database →
Replication).

Remaining stack:
[#49044](https://github.com/supabase/supabase/pull/49044) →
[#49045](https://github.com/supabase/supabase/pull/49045) →
[#49046](https://github.com/supabase/supabase/pull/49046) → this PR

## To test

`infrastructure:read_replicas` is an enabled-feature, on by default.
There is no Feature Preview or ConfigCat switch. You should already see
the Infrastructure Read replicas section. If you do not, your profile
lists `infrastructure:read_replicas` in `disabled_features`.

1.
[Infrastructure](https://studio-staging-git-dnywh-choreread-replicas-in-829a4b-supabase.vercel.app/dashboard/project/_/settings/infrastructure):
topology, Read replicas, Scaling.
2. Add read replica. If blocked on compute, Change to Small compute:
sheet closes, Small is selected and focused, the price footer is dirty,
and no blank page gap appears.
3. From the SQL editor database selector, Add replica should open
Infrastructure, not Replication.
2026-08-21 12:44:41 +10:00

132 lines
4.3 KiB
TypeScript

import { PermissionAction } from '@supabase/shared-types/out/constants'
import { useFlag, useParams } from 'common'
import Link from 'next/link'
import { PropsWithChildren } from 'react'
import { Button } from 'ui'
import { ButtonTooltip } from './ButtonTooltip'
import { RequestUpgradeToBillingOwners } from './RequestUpgradeToBillingOwners'
import { getInfrastructurePath } from '@/components/interfaces/Settings/Infrastructure/Infrastructure.utils'
import { SupportLink } from '@/components/interfaces/Support/SupportLink'
import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled'
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
export const PLAN_REQUEST_EMPTY_PLACEHOLDER =
'<Specify which plan to upgrade to: Pro | Team | Enterprise>'
interface UpgradePlanButtonProps {
/** Stick to camel case for consistency */
source: string
variant?: 'default' | 'primary'
plan?: 'Pro' | 'Team' | 'Enterprise'
addon?: 'pitr' | 'customDomain' | 'ipv4' | 'spendCap' | 'computeSize'
/** Used in the default message template for request upgrade dialog, e.g: "Upgrade to ..." */
featureProposition?: string
disabled?: boolean
className?: string
slug?: string
onClick?: () => void
}
/**
* If `billingAll` is enabled, links to upgrade paths (e.g organization settings, addons).
*
* Otherwise, links to support form instead
*/
export const UpgradePlanButton = ({
source,
variant = 'primary',
plan = 'Pro',
addon,
featureProposition,
disabled,
children,
className,
slug: slugParam,
onClick,
}: PropsWithChildren<UpgradePlanButtonProps>) => {
const { ref } = useParams()
const { data: organization } = useSelectedOrganizationQuery()
const isFreePlan = organization?.plan?.id === 'free'
const slug = slugParam ?? organization?.slug ?? '_'
const projectUpdateDisabled = useFlag('disableProjectCreationAndUpdate')
const { billingAll } = useIsFeatureEnabled(['billing:all'])
const { can: canUpdateSubscription } = useAsyncCheckPermissions(
PermissionAction.BILLING_WRITE,
'stripe.subscriptions',
undefined,
{ organizationSlug: slug }
)
const subject = `Enquiry to upgrade ${!!plan ? `to ${plan} ` : ''}plan for organization`
const message = `Name: ${organization?.name}\nSlug: ${slug}\nRequested plan: ${plan ?? PLAN_REQUEST_EMPTY_PLACEHOLDER}`
const isRequestingToDisableSpendCap = addon === 'spendCap'
const isOnPaidPlanAndRequestingToPurchaseAddon = !isFreePlan && !!addon
// [Joshen] URL for button based on the "upgrade request" and the org's plan. Falls back to URL for opening subscription plan
const href = isRequestingToDisableSpendCap
? `/org/${slug ?? '_'}/billing?panel=costControl&source=${source}`
: isOnPaidPlanAndRequestingToPurchaseAddon
? addon === 'computeSize'
? `${getInfrastructurePath(ref)}#compute`
: `/project/${ref ?? '_'}/settings/addons?panel=${addon}&source=${source}`
: `/org/${slug ?? '_'}/billing?panel=subscriptionPlan&source=${source}`
const linkChildren =
children ||
(isOnPaidPlanAndRequestingToPurchaseAddon
? addon === 'computeSize'
? 'Change compute size'
: 'Enable add-on'
: `Upgrade to ${plan}`)
const link = billingAll ? (
<Link href={href}>{linkChildren}</Link>
) : (
<SupportLink queryParams={{ orgSlug: slug, category: 'Plan_upgrade', subject, message }}>
{linkChildren}
</SupportLink>
)
if (!canUpdateSubscription) {
return (
<RequestUpgradeToBillingOwners
plan={plan}
addon={addon}
featureProposition={featureProposition}
className={className}
variant={variant}
>
{children}
</RequestUpgradeToBillingOwners>
)
}
if (projectUpdateDisabled) {
return (
<ButtonTooltip
disabled
variant={variant}
className={className}
tooltip={{
content: {
side: 'bottom',
text: 'Plan changes are currently disabled, our engineers are working on a fix',
},
}}
>
{linkChildren}
</ButtonTooltip>
)
}
return (
<Button asChild variant={variant} disabled={disabled} className={className} onClick={onClick}>
{link}
</Button>
)
}