mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 15:19:08 +03:00
change upgrade comms
This commit is contained in:
1 parent
e2d3a306e3
commit
b5fc1cd3ca
6 files changed
+111
-76
No files matched your search
@@ -49,6 +49,7 @@ export const calculateComputeSizePrice = ({
|
||||
availableOptions,
|
||||
oldComputeSize,
|
||||
newComputeSize,
|
||||
plan,
|
||||
}: {
|
||||
availableOptions: {
|
||||
identifier: string
|
||||
@@ -56,8 +57,19 @@ export const calculateComputeSizePrice = ({
|
||||
}[]
|
||||
oldComputeSize: string
|
||||
newComputeSize: string
|
||||
plan: PlanId
|
||||
}) => {
|
||||
const oldPrice = availableOptions?.find((x) => x.identifier === oldComputeSize)?.price ?? 0
|
||||
let _oldComputeSize = oldComputeSize
|
||||
|
||||
if (plan !== 'free') {
|
||||
/**
|
||||
* override the old compute size to micro if the plan is not free
|
||||
* this is to handle the case in which nano compute is a paid entity
|
||||
*/
|
||||
_oldComputeSize = 'ci_micro'
|
||||
}
|
||||
|
||||
const oldPrice = availableOptions?.find((x) => x.identifier === _oldComputeSize)?.price ?? 0
|
||||
const newPrice = availableOptions?.find((x) => x.identifier === newComputeSize)?.price ?? 0
|
||||
|
||||
const oldPriceMonthly = oldPrice * 720
|
||||
|
||||
@@ -90,14 +90,14 @@ export function DiskManagementForm() {
|
||||
*/
|
||||
const {
|
||||
data: databases,
|
||||
isLoading: isReadReplicasLoading,
|
||||
error: readReplicasError,
|
||||
// isLoading: isReadReplicasLoading,
|
||||
// error: readReplicasError,
|
||||
isSuccess: isReadReplicasSuccess,
|
||||
} = useReadReplicasQuery({ projectRef })
|
||||
const {
|
||||
data,
|
||||
isLoading: isDiskAttributesLoading,
|
||||
error: diskAttributesError,
|
||||
// isLoading: isDiskAttributesLoading,
|
||||
// error: diskAttributesError,
|
||||
isSuccess: isDiskAttributesSuccess,
|
||||
} = useDiskAttributesQuery(
|
||||
{ projectRef },
|
||||
@@ -125,28 +125,28 @@ export function DiskManagementForm() {
|
||||
}
|
||||
)
|
||||
const {
|
||||
isLoading: isAddonsLoading,
|
||||
error: addonsError,
|
||||
// isLoading: isAddonsLoading,
|
||||
// error: addonsError,
|
||||
isSuccess: isAddonsSuccess,
|
||||
} = useProjectAddonsQuery({ projectRef })
|
||||
const {
|
||||
isWithinCooldownWindow,
|
||||
isLoading: isCooldownLoading,
|
||||
// isLoading: isCooldownLoading,
|
||||
isSuccess: isCooldownSuccess,
|
||||
} = useRemainingDurationForDiskAttributeUpdate({
|
||||
projectRef,
|
||||
})
|
||||
const {
|
||||
isLoading: isDiskUtilizationLoading,
|
||||
error: diskUtilError,
|
||||
// isLoading: isDiskUtilizationLoading,
|
||||
// error: diskUtilError,
|
||||
isSuccess: isDiskUtilizationSuccess,
|
||||
} = useDiskUtilizationQuery({
|
||||
projectRef,
|
||||
})
|
||||
const {
|
||||
data: subscription,
|
||||
isLoading: isSubscriptionLoading,
|
||||
error: subscriptionError,
|
||||
// isLoading: isSubscriptionLoading,
|
||||
// error: subscriptionError,
|
||||
isSuccess: isSubscriptionSuccess,
|
||||
} = useOrgSubscriptionQuery({
|
||||
orgSlug: org?.slug,
|
||||
@@ -214,12 +214,12 @@ export function DiskManagementForm() {
|
||||
|
||||
const { mutateAsync: updateDiskConfiguration, isLoading: isUpdatingDisk } =
|
||||
useUpdateDiskAttributesMutation({
|
||||
// this is to supress to toast message
|
||||
// this is to suppress to toast message
|
||||
onError: () => {},
|
||||
})
|
||||
const { mutateAsync: updateSubscriptionAddon, isLoading: isUpdatingCompute } =
|
||||
useProjectAddonUpdateMutation({
|
||||
// this is to supress to toast message
|
||||
// this is to suppress to toast message
|
||||
onError: () => {},
|
||||
onSuccess: () => {
|
||||
/**
|
||||
|
||||
+10
-3
@@ -1,6 +1,6 @@
|
||||
import { PermissionAction } from '@supabase/shared-types/out/constants'
|
||||
import { ChevronRight } from 'lucide-react'
|
||||
import { useMemo } from 'react'
|
||||
import { ComponentProps, useMemo } from 'react'
|
||||
import { UseFormReturn } from 'react-hook-form'
|
||||
|
||||
import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext'
|
||||
@@ -39,11 +39,13 @@ import {
|
||||
calculateThroughputPrice,
|
||||
getAvailableComputeOptions,
|
||||
mapAddOnVariantIdToComputeSize,
|
||||
showMicroUpgrade,
|
||||
} from './DiskManagement.utils'
|
||||
import { DiskMangementRestartRequiredSection } from './DiskManagementRestartRequiredSection'
|
||||
import { BillingChangeBadge } from './ui/BillingChangeBadge'
|
||||
import { DiskType } from './ui/DiskManagement.constants'
|
||||
import { DiskMangementCoolDownSection } from './ui/DiskManagementCoolDownSection'
|
||||
import { InfraInstanceSize } from './DiskManagement.types'
|
||||
|
||||
const TableHeaderRow = () => (
|
||||
<TableRow>
|
||||
@@ -63,6 +65,7 @@ interface TableDataRowProps {
|
||||
afterPrice: number
|
||||
hidePrice?: boolean
|
||||
priceTooltip?: string
|
||||
upgradeIncluded?: boolean
|
||||
}
|
||||
|
||||
const TableDataRow = ({
|
||||
@@ -74,6 +77,7 @@ const TableDataRow = ({
|
||||
afterPrice,
|
||||
hidePrice = false,
|
||||
priceTooltip,
|
||||
upgradeIncluded = false,
|
||||
}: TableDataRowProps) => (
|
||||
<TableRow>
|
||||
<TableCell className="pl-5">
|
||||
@@ -101,14 +105,15 @@ const TableDataRow = ({
|
||||
</TableCell>
|
||||
<TableCell className="text-xs font-mono">{unit}</TableCell>
|
||||
<TableCell className="text-right pr-5">
|
||||
{hidePrice ? (
|
||||
{!upgradeIncluded && hidePrice ? (
|
||||
<span className="text-xs font-mono">-</span>
|
||||
) : beforePrice !== afterPrice ? (
|
||||
) : beforePrice !== afterPrice || upgradeIncluded ? (
|
||||
<BillingChangeBadge
|
||||
show={true}
|
||||
beforePrice={beforePrice}
|
||||
afterPrice={afterPrice}
|
||||
tooltip={priceTooltip}
|
||||
free={upgradeIncluded}
|
||||
/>
|
||||
) : (
|
||||
<span className="text-xs font-mono">{formatCurrency(beforePrice)}</span>
|
||||
@@ -179,6 +184,7 @@ export const DiskManagementReviewAndSubmitDialog = ({
|
||||
availableOptions,
|
||||
oldComputeSize: form.formState.defaultValues?.computeSize || 'ci_micro',
|
||||
newComputeSize: form.getValues('computeSize'),
|
||||
plan: subscription?.plan.id ?? 'free',
|
||||
})
|
||||
const diskSizePrice = calculateDiskSizePrice({
|
||||
planId: planId,
|
||||
@@ -265,6 +271,7 @@ export const DiskManagementReviewAndSubmitDialog = ({
|
||||
unit="-"
|
||||
beforePrice={Number(computeSizePrice.oldPrice)}
|
||||
afterPrice={Number(computeSizePrice.newPrice)}
|
||||
upgradeIncluded={true}
|
||||
/>
|
||||
)}
|
||||
{form.formState.defaultValues?.storageType !== form.getValues('storageType') && (
|
||||
|
||||
@@ -93,6 +93,7 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) {
|
||||
availableOptions: availableOptions,
|
||||
oldComputeSize: form.formState.defaultValues?.computeSize || 'ci_micro',
|
||||
newComputeSize: form.getValues('computeSize'),
|
||||
plan: subscription?.plan.id ?? 'free',
|
||||
})
|
||||
|
||||
const showUpgradeBadge = showMicroUpgrade(
|
||||
@@ -127,10 +128,10 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) {
|
||||
<NoticeBar
|
||||
showIcon={false}
|
||||
type="default"
|
||||
className="mt-3"
|
||||
visible={showUpgradeBadge && form.watch('computeSize') !== 'ci_micro'}
|
||||
title="Free Compute upgrade to Micro"
|
||||
description="Paid Plans include a free upgrade to Micro compute."
|
||||
className="mt-3 border-violet-900 bg-violet-200 [&_h5]:text-violet-1100"
|
||||
visible={showUpgradeBadge && form.watch('computeSize') === 'ci_nano'}
|
||||
title={'Upgrade to Micro Compute at no additional charge'}
|
||||
description="This Project is already paying for Micro Compute. You can upgrade to Micro Compute at any time when convenient."
|
||||
/>
|
||||
</>
|
||||
}
|
||||
@@ -166,72 +167,84 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) {
|
||||
project?.infra_compute_size !== 'nano' &&
|
||||
compute.identifier === 'ci_nano'
|
||||
|
||||
const price =
|
||||
subscription?.plan.id !== 'free' &&
|
||||
project?.infra_compute_size === 'nano' &&
|
||||
compute.identifier === 'ci_nano'
|
||||
? availableOptions.find(
|
||||
(option: ComputeOption) => option.identifier === 'ci_micro'
|
||||
)?.price
|
||||
: compute.price
|
||||
|
||||
return (
|
||||
<RadioGroupCardItem
|
||||
key={compute.identifier}
|
||||
showIndicator={false}
|
||||
value={compute.identifier}
|
||||
className={cn(
|
||||
'text-sm text-left flex flex-col gap-0 px-0 py-3 overflow-hidden [&_label]:w-full group] w-full h-[110px]',
|
||||
'relative text-sm text-left flex flex-col gap-0 px-0 py-3 [&_label]:w-full group] w-full h-[110px]',
|
||||
lockedOption && 'opacity-50'
|
||||
)}
|
||||
disabled={disabled || lockedOption}
|
||||
// @ts-expect-error
|
||||
label={
|
||||
<div className="w-full flex flex-col gap-3 justify-between">
|
||||
<div className="px-3 opacity-50 group-data-[state=checked]:opacity-100 flex justify-between">
|
||||
<ComputeBadge
|
||||
className="inline-flex font-semibold"
|
||||
infraComputeSize={
|
||||
compute.name as components['schemas']['DbInstanceSize']
|
||||
}
|
||||
/>
|
||||
<div className="flex items-center space-x-1">
|
||||
{lockedOption ? (
|
||||
<div className="bg border rounded-lg h-7 w-7 flex items-center justify-center">
|
||||
<Lock size={14} />
|
||||
</div>
|
||||
) : showUpgradeBadge && compute.identifier === 'ci_micro' ? (
|
||||
<div className="w-full text-warning-600 flex items-center gap-1 bg-warning-200 py-0.5 px-2 rounded-full border border-warning-500">
|
||||
<span className="font-mono">FREE UPGRADE</span>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-foreground text-sm font-semibold">
|
||||
${compute.price}
|
||||
</span>
|
||||
<span className="text-foreground-light translate-y-[1px]">
|
||||
{' '}
|
||||
/ {compute.price_interval === 'monthly' ? 'month' : 'hour'}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
{showUpgradeBadge && compute.identifier === 'ci_micro' && (
|
||||
<div className="absolute -top-4 -right-3 text-violet-1100 flex items-center gap-1 bg-surface-75 py-0.5 px-2 rounded-full border border-violet-900">
|
||||
<span>No additional charge</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full flex flex-col gap-3 justify-between">
|
||||
<div className="relative px-3 opacity-50 group-data-[state=checked]:opacity-100 flex justify-between">
|
||||
<ComputeBadge
|
||||
className="inline-flex font-semibold"
|
||||
infraComputeSize={
|
||||
compute.name as components['schemas']['DbInstanceSize']
|
||||
}
|
||||
/>
|
||||
<div className="flex items-center space-x-1">
|
||||
{lockedOption ? (
|
||||
<div className="bg border rounded-lg h-7 w-7 flex items-center justify-center">
|
||||
<Lock size={14} />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<span className="text-foreground text-sm font-semibold">
|
||||
${price}
|
||||
</span>
|
||||
<span className="text-foreground-light translate-y-[1px]">
|
||||
{' '}
|
||||
/ {compute.price_interval === 'monthly' ? 'month' : 'hour'}
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full">
|
||||
<div className="px-3 text-sm flex flex-col gap-1">
|
||||
<div className="text-foreground-light flex gap-2 items-center">
|
||||
<Microchip
|
||||
strokeWidth={1}
|
||||
size={14}
|
||||
className="text-foreground-lighter"
|
||||
/>
|
||||
<span>{compute.meta?.memory_gb ?? 0} GB memory</span>
|
||||
</div>
|
||||
<div className="text-foreground-light flex gap-2 items-center">
|
||||
<CpuIcon
|
||||
strokeWidth={1}
|
||||
size={14}
|
||||
className="text-foreground-lighter"
|
||||
/>
|
||||
<span>
|
||||
{compute.meta?.cpu_cores ?? 0}-core {cpuArchitecture} CPU
|
||||
</span>
|
||||
<div className="w-full">
|
||||
<div className="px-3 text-sm flex flex-col gap-1">
|
||||
<div className="text-foreground-light flex gap-2 items-center">
|
||||
<Microchip
|
||||
strokeWidth={1}
|
||||
size={14}
|
||||
className="text-foreground-lighter"
|
||||
/>
|
||||
<span>{compute.meta?.memory_gb ?? 0} GB memory</span>
|
||||
</div>
|
||||
<div className="text-foreground-light flex gap-2 items-center">
|
||||
<CpuIcon
|
||||
strokeWidth={1}
|
||||
size={14}
|
||||
className="text-foreground-lighter"
|
||||
/>
|
||||
<span>
|
||||
{compute.meta?.cpu_cores ?? 0}-core {cpuArchitecture} CPU
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -36,8 +36,8 @@ const BillingChangeBadge = ({
|
||||
<Badge
|
||||
variant="default"
|
||||
className={cn(
|
||||
!free ? `bg-alternative` : `bg-warning-200 border-warning-500 text-warning`,
|
||||
`bg-opacity-100`,
|
||||
!free ? `bg-alternative` : `bg-violet-200 border-violet-900 text-violet-1100`,
|
||||
`bg-opacity-100 text-warning`,
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -50,11 +50,14 @@ const BillingChangeBadge = ({
|
||||
<ChevronRight size={12} strokeWidth={2} className="text-foreground-muted" />
|
||||
<motion.span
|
||||
key={afterPrice} // This key will change whenever any form value changes
|
||||
className="text-xs font-mono text-foreground"
|
||||
className={cn(
|
||||
free ? 'text-violet-1100' : 'text-foreground',
|
||||
'text-xs font-mono'
|
||||
)}
|
||||
animate={{ scale: [1, 1.1, 1] }}
|
||||
transition={{ duration: 0.12 }}
|
||||
>
|
||||
{free ? 'Free' : `${formatCurrency(afterPrice)}/month`}
|
||||
{`${formatCurrency(afterPrice)}/month`}
|
||||
</motion.span>
|
||||
</div>
|
||||
</TooltipTrigger_Shadcn_>
|
||||
|
||||
@@ -233,7 +233,7 @@ const Addons = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!diskAndComputeFormEnabled ? (
|
||||
{true ? (
|
||||
<ProjectUpdateDisabledTooltip
|
||||
projectUpdateDisabled={projectUpdateDisabled || computeSizeChangesDisabled}
|
||||
projectNotActive={!isProjectActive}
|
||||
|
||||
Reference in new issue
Block a user