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:
Joshen Lim authored and GitHub committed 2024-09-17 15:22:18 +08:00
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'],
})
}
@@ -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 (
<>