mirror of
https://github.com/supabase/supabase.git
synced 2026-06-13 01:39:53 +08:00
## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? This takes our dialog to review compute upgrade's and gives it a bit more visual clarity (as it can get confusing). Simplification and clear communication of what differences there are. | Before | After | |--------|--------| | <img width="663" height="722" alt="Screenshot 2026-04-07 at 16 58 33" src="https://github.com/user-attachments/assets/dbb699b4-89ad-4172-8c23-e5d1ca5045f8" /> | <img width="608" height="635" alt="Screenshot 2026-04-08 at 12 28 32" src="https://github.com/user-attachments/assets/9a4a5952-6049-4cda-86e6-73773f001010" /> | <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **UI Improvements** * Redesigned disk review dialog to a stacked "Before/After" layout with a clear vertical breakdown of changes. * Replaced badges/tooltips with inline per-line monthly deltas and explicit summed totals. * Replica note moved inline; cooldown and throughput explanations consolidated and more broadly shown when storage type changes. * Arrow between totals updated and "After" state emphasized. * **Chores** * Updated dialog spacing, padding, min-width, confirm button styling, and dialog wording. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
187 lines
6.1 KiB
TypeScript
187 lines
6.1 KiB
TypeScript
import { useMemo } from 'react'
|
|
import { UseFormReturn } from 'react-hook-form'
|
|
|
|
import { DiskStorageSchemaType } from '../DiskManagement.schema'
|
|
import {
|
|
calculateComputeSizePrice,
|
|
calculateDiskSizePrice,
|
|
calculateIOPSPrice,
|
|
calculateThroughputPrice,
|
|
getAvailableComputeOptions,
|
|
mapAddOnVariantIdToComputeSize,
|
|
} from '../DiskManagement.utils'
|
|
import { DiskType } from '../ui/DiskManagement.constants'
|
|
import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query'
|
|
import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization'
|
|
import {
|
|
useIsAwsNimbusCloudProvider,
|
|
useSelectedProjectQuery,
|
|
} from '@/hooks/misc/useSelectedProject'
|
|
|
|
export function useDiskManagementReviewChanges(
|
|
form: UseFormReturn<DiskStorageSchemaType>,
|
|
numReplicas: number
|
|
) {
|
|
const { data: project } = useSelectedProjectQuery()
|
|
const { data: org } = useSelectedOrganizationQuery()
|
|
const isAwsNimbus = useIsAwsNimbusCloudProvider()
|
|
const { data: addons } = useProjectAddonsQuery({ projectRef: project?.ref })
|
|
|
|
const isAwsK8sProject = project?.cloud_provider === 'AWS_K8S'
|
|
const planId = org?.plan.id ?? 'free'
|
|
|
|
const availableAddons = useMemo(() => addons?.available_addons ?? [], [addons])
|
|
const availableOptions = useMemo(
|
|
() => getAvailableComputeOptions(availableAddons, project?.cloud_provider),
|
|
[availableAddons, project?.cloud_provider]
|
|
)
|
|
|
|
// --- Prices ---
|
|
|
|
const computeSizePrice = calculateComputeSizePrice({
|
|
availableOptions,
|
|
oldComputeSize: form.formState.defaultValues?.computeSize || 'ci_micro',
|
|
newComputeSize: form.getValues('computeSize'),
|
|
plan: planId,
|
|
})
|
|
const diskSizePrice = calculateDiskSizePrice({
|
|
planId,
|
|
oldSize: form.formState.defaultValues?.totalSize || 0,
|
|
oldStorageType: form.formState.defaultValues?.storageType as DiskType,
|
|
newSize: form.getValues('totalSize'),
|
|
newStorageType: form.getValues('storageType') as DiskType,
|
|
numReplicas,
|
|
})
|
|
const iopsPrice = calculateIOPSPrice({
|
|
oldStorageType: form.formState.defaultValues?.storageType as DiskType,
|
|
oldProvisionedIOPS: form.formState.defaultValues?.provisionedIOPS || 0,
|
|
newStorageType: form.getValues('storageType') as DiskType,
|
|
newProvisionedIOPS: form.getValues('provisionedIOPS'),
|
|
numReplicas,
|
|
})
|
|
const throughputPrice = calculateThroughputPrice({
|
|
storageType: form.getValues('storageType') as DiskType,
|
|
newThroughput: form.getValues('throughput') || 0,
|
|
oldThroughput: form.formState.defaultValues?.throughput || 0,
|
|
numReplicas,
|
|
})
|
|
|
|
const totalBeforePrice =
|
|
Number(computeSizePrice.oldPrice) +
|
|
Number(diskSizePrice.oldPrice) +
|
|
Number(iopsPrice.oldPrice) +
|
|
Number(throughputPrice.oldPrice)
|
|
|
|
const totalAfterPrice =
|
|
Number(computeSizePrice.newPrice) +
|
|
Number(diskSizePrice.newPrice) +
|
|
Number(iopsPrice.newPrice) +
|
|
Number(throughputPrice.newPrice)
|
|
|
|
// --- Change flags ---
|
|
|
|
const hasComputeChanges =
|
|
form.formState.defaultValues?.computeSize !== form.getValues('computeSize')
|
|
|
|
const hasTotalSizeChanges =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
form.formState.defaultValues?.totalSize !== form.getValues('totalSize')
|
|
|
|
const hasStorageTypeChanges =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
form.formState.defaultValues?.storageType !== form.getValues('storageType')
|
|
|
|
const hasThroughputChanges =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
form.formState.defaultValues?.throughput !== form.getValues('throughput')
|
|
|
|
const hasIOPSChanges =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
form.formState.defaultValues?.provisionedIOPS !== form.getValues('provisionedIOPS')
|
|
|
|
const hasGrowthPercentChanges =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
form.formState.defaultValues?.growthPercent !== form.getValues('growthPercent')
|
|
|
|
const hasMinIncrementChanges =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
form.formState.defaultValues?.minIncrementGb !== form.getValues('minIncrementGb')
|
|
|
|
const hasMaxSizeChanges =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
form.formState.defaultValues?.maxSizeGb !== form.getValues('maxSizeGb')
|
|
|
|
// --- Derived predicates ---
|
|
|
|
const storageTypeBefore = (form.formState.defaultValues?.storageType ?? '') as DiskType
|
|
const storageTypeAfter = form.getValues('storageType') as DiskType
|
|
|
|
// Show hero whenever any line-item price actually changes, not just compute
|
|
const anyBillableDiskChange =
|
|
Number(diskSizePrice.newPrice) !== Number(diskSizePrice.oldPrice) ||
|
|
Number(iopsPrice.newPrice) !== Number(iopsPrice.oldPrice) ||
|
|
Number(throughputPrice.newPrice) !== Number(throughputPrice.oldPrice)
|
|
|
|
// Show cooldown warning whenever any disk attribute that enforces the 4-hour lock changes
|
|
const anyDiskAttributeChange = hasIOPSChanges || hasStorageTypeChanges || hasTotalSizeChanges
|
|
|
|
// Show throughput row whenever either the before or after storage type is GP3
|
|
// (covers GP3→IO2 where the throughput charge drops to zero)
|
|
const showThroughputRow =
|
|
!isAwsK8sProject &&
|
|
!isAwsNimbus &&
|
|
(storageTypeBefore === 'gp3' || storageTypeAfter === 'gp3') &&
|
|
(hasThroughputChanges || hasStorageTypeChanges)
|
|
|
|
const hasAnyBreakdownRows =
|
|
hasComputeChanges ||
|
|
hasStorageTypeChanges ||
|
|
hasIOPSChanges ||
|
|
showThroughputRow ||
|
|
hasTotalSizeChanges ||
|
|
hasGrowthPercentChanges ||
|
|
hasMinIncrementChanges ||
|
|
hasMaxSizeChanges
|
|
|
|
// --- Labels ---
|
|
|
|
const oldComputeLabel = mapAddOnVariantIdToComputeSize(
|
|
form.formState.defaultValues?.computeSize ?? 'ci_nano'
|
|
)
|
|
const newComputeLabel = mapAddOnVariantIdToComputeSize(form.getValues('computeSize'))
|
|
|
|
return {
|
|
// prices
|
|
computeSizePrice,
|
|
diskSizePrice,
|
|
iopsPrice,
|
|
throughputPrice,
|
|
totalBeforePrice,
|
|
totalAfterPrice,
|
|
// change flags
|
|
hasComputeChanges,
|
|
hasTotalSizeChanges,
|
|
hasStorageTypeChanges,
|
|
hasThroughputChanges,
|
|
hasIOPSChanges,
|
|
hasGrowthPercentChanges,
|
|
hasMinIncrementChanges,
|
|
hasMaxSizeChanges,
|
|
// derived predicates
|
|
anyBillableDiskChange,
|
|
anyDiskAttributeChange,
|
|
showThroughputRow,
|
|
hasAnyBreakdownRows,
|
|
// labels
|
|
oldComputeLabel,
|
|
newComputeLabel,
|
|
}
|
|
}
|