mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 17:35:10 +03:00
Chore/more disk mgt UI updates (#29333)
* Only show new disk billing UI for AWS provider projects * Fix DiskSpaceBar autoscaling should be fixed at 90% * Change all MiB to MB and GiB to GB * Prettier
This commit is contained in:
1 parent
7f0a216c40
commit
71a184738c
8 files changed
+40
-37
No files matched your search
@@ -1,5 +1,8 @@
|
||||
import { PlanType } from 'data/subscriptions/org-subscription-query'
|
||||
|
||||
// Disk Storage expands automatically when the database reaches 90% of the disk size
|
||||
export const AUTOSCALING_THRESHOLD = 0.9
|
||||
|
||||
export enum DiskType {
|
||||
GP3 = 'gp3',
|
||||
IO2 = 'io2',
|
||||
@@ -16,12 +19,12 @@ export const THROUGHPUT_RANGE = {
|
||||
|
||||
export const DISK_PRICING = {
|
||||
[DiskType.GP3]: {
|
||||
storage: 0.125, // per GiB per month
|
||||
storage: 0.125, // per GB per month
|
||||
iops: 0.024, // per IOPS per month, charged after 3000 IOPS
|
||||
throughput: 0.095, // per MB/s per month, charged after 125 MB/s
|
||||
},
|
||||
[DiskType.IO2]: {
|
||||
storage: 0.195, // per GiB per month
|
||||
storage: 0.195, // per GB per month
|
||||
iops: 0.119, // per IOPS per month
|
||||
},
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('DiskManagement.utils.ts:calculateIOPSPrice', () => {
|
||||
})
|
||||
|
||||
describe('DiskManagement.utils.ts:calculateThroughputPrice', () => {
|
||||
test('GP3 with 125 MiBps 150 MiBps', () => {
|
||||
test('GP3 with 125 MB/s 150 MB/s', () => {
|
||||
const result = calculateThroughputPrice({
|
||||
storageType: DiskType.GP3,
|
||||
oldThroughput: 125,
|
||||
@@ -73,7 +73,7 @@ describe('DiskManagement.utils.ts:calculateThroughputPrice', () => {
|
||||
expect(result.oldPrice).toBe('0.00')
|
||||
expect(result.newPrice).toBe('2.38')
|
||||
})
|
||||
test('IO1 with 125 MiBps 150 MiBps', () => {
|
||||
test('IO1 with 125 MB/s 150 MB/s', () => {
|
||||
const result = calculateThroughputPrice({
|
||||
storageType: DiskType.IO2,
|
||||
oldThroughput: 125,
|
||||
|
||||
@@ -13,14 +13,14 @@ export const calculateDiskSizePrice = ({
|
||||
newSize: number
|
||||
newStorageType: DiskType
|
||||
}) => {
|
||||
const oldPricePerGiB = DISK_PRICING[oldStorageType]?.storage ?? 0
|
||||
const newPricePerGiB = DISK_PRICING[newStorageType]?.storage ?? 0
|
||||
const oldPricePerGB = DISK_PRICING[oldStorageType]?.storage ?? 0
|
||||
const newPricePerGB = DISK_PRICING[newStorageType]?.storage ?? 0
|
||||
const { includedDiskGB } = PLAN_DETAILS?.[planId as keyof typeof PLAN_DETAILS] ?? {}
|
||||
|
||||
const oldPrice = (Math.max(oldSize - includedDiskGB[oldStorageType], 0) * oldPricePerGiB).toFixed(
|
||||
const oldPrice = (Math.max(oldSize - includedDiskGB[oldStorageType], 0) * oldPricePerGB).toFixed(
|
||||
2
|
||||
)
|
||||
const newPrice = (Math.max(newSize - includedDiskGB[newStorageType], 0) * newPricePerGiB).toFixed(
|
||||
const newPrice = (Math.max(newSize - includedDiskGB[newStorageType], 0) * newPricePerGB).toFixed(
|
||||
2
|
||||
)
|
||||
|
||||
@@ -62,7 +62,7 @@ export const calculateIOPSPrice = ({
|
||||
}
|
||||
|
||||
// This is only applicable for GP3 storage type, no need to consider IO2 at all
|
||||
// Also assumes that disk size is > 400 GiB (separate requirement to update throughput)
|
||||
// Also assumes that disk size is > 400 GB (separate requirement to update throughput)
|
||||
export const calculateThroughputPrice = ({
|
||||
storageType,
|
||||
newThroughput,
|
||||
|
||||
@@ -210,7 +210,7 @@ export function DiskManagementPanelForm() {
|
||||
if (watchedStorageType === 'io2') {
|
||||
setValue('throughput', undefined) // Throughput is not configurable for 'io2'
|
||||
} else if (watchedStorageType === 'gp3') {
|
||||
// Ensure throughput is within the allowed range if it's greater than or equal to 400 GiB
|
||||
// Ensure throughput is within the allowed range if it's greater than or equal to 400 GB
|
||||
const currentThroughput = form.getValues('throughput')
|
||||
const { min, max } = THROUGHPUT_RANGE[DiskType.GP3]
|
||||
if (!currentThroughput || currentThroughput < min || currentThroughput > max) {
|
||||
@@ -382,7 +382,7 @@ export function DiskManagementPanelForm() {
|
||||
</TooltipTrigger_Shadcn_>
|
||||
<TooltipContent_Shadcn_ side="bottom">
|
||||
For io2 storage type, min IOPS is at {minIOPS}, while max IOPS is
|
||||
at 1000 * disk size in GiB or{' '}
|
||||
at 1000 * disk size in GB or{' '}
|
||||
{IOPS_RANGE[DiskType.IO2].max.toLocaleString()}, whichever is
|
||||
lower
|
||||
</TooltipContent_Shadcn_>
|
||||
@@ -405,7 +405,7 @@ export function DiskManagementPanelForm() {
|
||||
</TooltipTrigger_Shadcn_>
|
||||
<TooltipContent_Shadcn_ side="bottom" className="w-64">
|
||||
For gp3 storage type, min IOPS is at {minIOPS} while max IOPS is
|
||||
at 500 * disk size in GiB or{' '}
|
||||
at 500 * disk size in GB or{' '}
|
||||
{IOPS_RANGE[DiskType.GP3].max.toLocaleString()}, whichever is
|
||||
lower
|
||||
</TooltipContent_Shadcn_>
|
||||
@@ -474,14 +474,14 @@ export function DiskManagementPanelForm() {
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormItemLayout
|
||||
label="Throughput (MiBps)"
|
||||
label="Throughput (MB/s)"
|
||||
layout="horizontal"
|
||||
description={
|
||||
<>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<span>
|
||||
Throughput must be between {minThroughput} and {maxThroughput}{' '}
|
||||
MiBps based on your IOPS.
|
||||
MB/s based on your IOPS.
|
||||
</span>
|
||||
<Tooltip_Shadcn_>
|
||||
<TooltipTrigger_Shadcn_ asChild>
|
||||
@@ -491,8 +491,8 @@ export function DiskManagementPanelForm() {
|
||||
/>
|
||||
</TooltipTrigger_Shadcn_>
|
||||
<TooltipContent_Shadcn_ side="bottom" className="w-64">
|
||||
Min throughput is at 125MiBps, while max throughput is at
|
||||
0.25MiBps * IOPS or 1000, whichever is lower
|
||||
Min throughput is at 125MB/s, while max throughput is at
|
||||
0.25MB/s * IOPS or 1000, whichever is lower
|
||||
</TooltipContent_Shadcn_>
|
||||
</Tooltip_Shadcn_>
|
||||
</div>
|
||||
@@ -503,7 +503,7 @@ export function DiskManagementPanelForm() {
|
||||
<span className="text-foreground">
|
||||
{maxThroughputBasedOnCompute.toFixed(0)}
|
||||
</span>{' '}
|
||||
MiBps based on your current compute size of{' '}
|
||||
MB/s based on your current compute size of{' '}
|
||||
{currentCompute?.name}
|
||||
</p>
|
||||
)}
|
||||
@@ -528,7 +528,7 @@ export function DiskManagementPanelForm() {
|
||||
</FormControl_Shadcn_>
|
||||
<div className="border border-strong bg-surface-300 rounded-r-md px-3 flex items-center justify-center">
|
||||
<span className="text-foreground-lighter text-xs font-mono">
|
||||
MiBps
|
||||
MB/s
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -560,7 +560,7 @@ export function DiskManagementPanelForm() {
|
||||
layout="horizontal"
|
||||
description={
|
||||
includedDiskGB > 0 &&
|
||||
`Your plan includes ${includedDiskGB} GiB of disk size for ${watchedStorageType}.`
|
||||
`Your plan includes ${includedDiskGB} GB of disk size for ${watchedStorageType}.`
|
||||
}
|
||||
>
|
||||
<div className="mt-1 relative flex gap-2 items-center">
|
||||
@@ -585,7 +585,7 @@ export function DiskManagementPanelForm() {
|
||||
/>
|
||||
</FormControl_Shadcn_>
|
||||
<div className="border border-strong bg-surface-300 rounded-r-md px-3 flex items-center justify-center">
|
||||
<span className="text-foreground-lighter text-xs font-mono">GiB</span>
|
||||
<span className="text-foreground-lighter text-xs font-mono">GB</span>
|
||||
</div>
|
||||
</div>
|
||||
<AnimatePresence initial={false}>
|
||||
|
||||
@@ -4,11 +4,11 @@ const baseSchema = z.object({
|
||||
storageType: z.enum(['io2', 'gp3']).describe('Type of storage: io2 or gp3'),
|
||||
totalSize: z
|
||||
.number()
|
||||
.min(8, { message: 'Allocated disk size must be at least 8 GiB.' })
|
||||
.max(16384, { message: 'Allocated disk size must not exceed 16,384 GiB.' })
|
||||
.describe('Allocated disk size in GiB'),
|
||||
.min(8, { message: 'Allocated disk size must be at least 8 GB.' })
|
||||
.max(16384, { message: 'Allocated disk size must not exceed 16,384 GB.' })
|
||||
.describe('Allocated disk size in GB'),
|
||||
provisionedIOPS: z.number().describe('Provisioned IOPS for storage type'),
|
||||
throughput: z.number().optional().describe('Throughput in MiBps for gp3'),
|
||||
throughput: z.number().optional().describe('Throughput in MB/s for gp3'),
|
||||
})
|
||||
|
||||
export const DiskStorageSchema = baseSchema.superRefine((data, ctx) => {
|
||||
@@ -75,7 +75,7 @@ export const DiskStorageSchema = baseSchema.superRefine((data, ctx) => {
|
||||
if (throughput !== undefined && (throughput < 125 || throughput > maxThroughput)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Throughput must be set between 125 and ${maxThroughput} MiBps.`,
|
||||
message: `Throughput must be set between 125 and ${maxThroughput} MB/s.`,
|
||||
path: ['throughput'],
|
||||
})
|
||||
}
|
||||
|
||||
+2
-2
@@ -164,7 +164,7 @@ export const DiskManagementReviewAndSubmitDialog = ({
|
||||
attribute="Disk size"
|
||||
defaultValue={form.formState.defaultValues?.totalSize ?? 0}
|
||||
newValue={form.getValues('totalSize')}
|
||||
unit="GiB"
|
||||
unit="GB"
|
||||
beforePrice={Number(diskSizePrice.oldPrice)}
|
||||
afterPrice={Number(diskSizePrice.newPrice)}
|
||||
/>
|
||||
@@ -181,7 +181,7 @@ export const DiskManagementReviewAndSubmitDialog = ({
|
||||
attribute="Throughput"
|
||||
defaultValue={form.formState.defaultValues?.throughput ?? 0}
|
||||
newValue={form.getValues('throughput') ?? 0}
|
||||
unit="MiBps"
|
||||
unit="MB/s"
|
||||
beforePrice={Number(throughputPrice.oldPrice)}
|
||||
afterPrice={Number(throughputPrice.newPrice)}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import MotionNumber from 'motion-number'
|
||||
import { useState } from 'react'
|
||||
|
||||
import {
|
||||
badgeVariants,
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
TooltipContent_Shadcn_,
|
||||
TooltipTrigger_Shadcn_,
|
||||
} from 'ui'
|
||||
import { AUTOSCALING_THRESHOLD } from './DiskManagement.constants'
|
||||
|
||||
interface DiskSpaceBarProps {
|
||||
showNewBar: boolean
|
||||
@@ -23,21 +23,17 @@ export default function DiskSpaceBar({
|
||||
usedSize,
|
||||
newTotalSize,
|
||||
}: DiskSpaceBarProps) {
|
||||
const [resizeThreshold, setResizeThreshold] = useState<number>(0.5) // 500 MB threshold for resize
|
||||
|
||||
const usedPercentage = (usedSize / totalSize) * 100
|
||||
const resizePoint = totalSize - resizeThreshold
|
||||
const resizePercentage = (resizePoint / totalSize) * 100
|
||||
const resizePercentage = AUTOSCALING_THRESHOLD * 100
|
||||
|
||||
const newUsedPercentage = (usedSize / newTotalSize) * 100
|
||||
const newResizePoint = newTotalSize - resizeThreshold
|
||||
const newResizePercentage = (newResizePoint / newTotalSize) * 100
|
||||
const newResizePercentage = AUTOSCALING_THRESHOLD * 100
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex items-center h-6 gap-3">
|
||||
<span className="text-foreground-light text-sm font-mono flex items-center gap-2">
|
||||
{usedSize.toFixed(2)} GiB used of{' '}
|
||||
{usedSize.toFixed(2)} GB used of{' '}
|
||||
<span className="text-foreground font-semibold -mt-[2px]">
|
||||
<MotionNumber
|
||||
value={newTotalSize}
|
||||
@@ -48,7 +44,7 @@ export default function DiskSpaceBar({
|
||||
className="font-mono"
|
||||
/>
|
||||
</span>{' '}
|
||||
GiB
|
||||
GB
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative">
|
||||
|
||||
@@ -17,14 +17,18 @@ import DiskSizeConfiguration from 'components/interfaces/Settings/Database/DiskS
|
||||
import { useFlag } from 'hooks/ui/useFlag'
|
||||
import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useSelectedProject } from 'hooks/misc/useSelectedProject'
|
||||
|
||||
const ProjectSettings: NextPageWithLayout = () => {
|
||||
const diskManagementV2 = useFlag('diskManagementV2')
|
||||
const project = useSelectedProject()
|
||||
const selectedOrg = useSelectedOrganization()
|
||||
const { data: subscription } = useOrgSubscriptionQuery({ orgSlug: selectedOrg?.slug })
|
||||
|
||||
const showNewDiskManagementUI =
|
||||
subscription?.usage_based_billing_project_addons && diskManagementV2
|
||||
diskManagementV2 &&
|
||||
project?.cloud_provider === 'AWS' &&
|
||||
subscription?.usage_based_billing_project_addons
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
Reference in new issue
Block a user