mirror of
https://github.com/supabase/supabase.git
synced 2026-10-17 15:19:08 +03:00
updated compute form. moved warning panels. added collapsible for advanced options
This commit is contained in:
1 parent
6afbd65182
commit
be9ba1c488
5 files changed
+664
-547
No files matched your search
@@ -0,0 +1,64 @@
|
||||
import { ScaffoldContainer } from 'components/layouts/Scaffold'
|
||||
import { Form_Shadcn_, Separator, Skeleton } from 'ui'
|
||||
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
|
||||
|
||||
export function DiskManagementFormLoading() {
|
||||
return (
|
||||
<Form_Shadcn_>
|
||||
<form className="flex flex-col gap-8">
|
||||
<ScaffoldContainer className="relative flex flex-col gap-10" bottomPadding>
|
||||
<Separator />
|
||||
|
||||
<FormItemLayout
|
||||
label="Compute size"
|
||||
layout="horizontal"
|
||||
isReactForm={false}
|
||||
labelOptional={<p>Hardware resources allocated to your postgres database</p>}
|
||||
>
|
||||
<div className="grid grid-cols-3 flex-wrap gap-3">
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-[110px]" />
|
||||
</div>
|
||||
</FormItemLayout>
|
||||
|
||||
<Separator />
|
||||
|
||||
<FormItemLayout label="Storage type" layout="horizontal" isReactForm={false}>
|
||||
<div className="grid grid-cols-2 flex-wrap gap-3">
|
||||
<Skeleton className="bg-surface-300 block w-full h-21" />
|
||||
<Skeleton className="bg-surface-300 block w-full h-21" />
|
||||
</div>
|
||||
</FormItemLayout>
|
||||
|
||||
{/* <FormItemLayout
|
||||
label="Compute size"
|
||||
layout="horizontal"
|
||||
isReactForm={false}
|
||||
labelOptional="hardware resources allocated to your postgres database"
|
||||
>
|
||||
<Skeleton className="bg-surface-300 w-100 h-12" />
|
||||
<Skeleton className="bg-surface-300 w-100 h-12" />
|
||||
<Skeleton className="bg-surface-300 w-100 h-12" />
|
||||
</FormItemLayout>
|
||||
|
||||
<FormItemLayout
|
||||
label="Compute size"
|
||||
layout="horizontal"
|
||||
isReactForm={false}
|
||||
labelOptional="hardware resources allocated to your postgres database"
|
||||
>
|
||||
<Skeleton className="bg-surface-300 w-100 h-10" />
|
||||
</FormItemLayout> */}
|
||||
</ScaffoldContainer>
|
||||
</form>
|
||||
</Form_Shadcn_>
|
||||
)
|
||||
}
|
||||
@@ -5,25 +5,35 @@ import { Card, CardContent } from 'ui'
|
||||
import CountdownTimerRadial from '../../ui/CountdownTimer/CountdownTimerRadial'
|
||||
import CountdownTimerSpan from '../../ui/CountdownTimer/CountdownTimerSpan'
|
||||
|
||||
export function DiskCountdownRadial({ remainingTime }: { remainingTime: number }) {
|
||||
export function DiskCountdownRadial({
|
||||
remainingTime,
|
||||
show = true,
|
||||
}: {
|
||||
remainingTime: number
|
||||
show?: boolean
|
||||
}) {
|
||||
const progressPercentage = (remainingTime / COOLDOWN_DURATION) * 100
|
||||
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{remainingTime !== 0 && (
|
||||
{remainingTime !== 0 && show && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{ opacity: 1, height: 'auto' }}
|
||||
exit={{ opacity: 0, height: 0 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
>
|
||||
<Card className="px-2 rounded-none">
|
||||
<Card className="px-2 rounded bg-surface-100">
|
||||
<CardContent className="py-3 flex gap-3 px-3 items-center">
|
||||
<CountdownTimerRadial progress={progressPercentage} />
|
||||
<div className="flex flex-col">
|
||||
<p className="text-foreground-lighter text-sm p-0">
|
||||
<p className="text-foreground text-sm p-0">
|
||||
6-hour wait period between disk modifications
|
||||
</p>
|
||||
<p className="text-foreground-lighter text-sm p-0">
|
||||
You can't modify your disk configuration until the 6-hour cool down period ends.
|
||||
You can however change the compute size..
|
||||
</p>
|
||||
<CountdownTimerSpan seconds={remainingTime} />
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -13,94 +13,104 @@ const baseSchema = z.object({
|
||||
computeSize: z.string().describe('Compute size'),
|
||||
})
|
||||
|
||||
export const DiskStorageSchema = baseSchema.superRefine((data, ctx) => {
|
||||
const { storageType, totalSize, provisionedIOPS, throughput, computeSize } = data
|
||||
export const CreateDiskStorageSchema = (defaultTotalSize: number) => {
|
||||
const schema = baseSchema.superRefine((data, ctx) => {
|
||||
const { storageType, totalSize, provisionedIOPS, throughput, computeSize } = data
|
||||
|
||||
// if(computeSize)
|
||||
|
||||
if (storageType === 'io2') {
|
||||
// Validation rules for io2
|
||||
const maxIOPS = calculateMaxIopsAllowed(totalSize)
|
||||
|
||||
if (provisionedIOPS < 100) {
|
||||
if (totalSize < defaultTotalSize) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Provisioned IOPS must be at least 100',
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
} else if (provisionedIOPS > maxIOPS) {
|
||||
if (totalSize >= 8) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Provisioned IOPS must be at most ${maxIOPS}.`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
} else {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Invalid IOPS value due to small disk size`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (throughput !== undefined) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Throughput is not configurable for io2.',
|
||||
path: ['throughput'],
|
||||
})
|
||||
}
|
||||
|
||||
if (totalSize > DISK_LIMITS[DiskType.IO2].maxStorage) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Allocated disksize must not exceed 61,440 GB',
|
||||
message: `Allocated disk size must be at least ${defaultTotalSize} GB.`,
|
||||
path: ['totalSize'],
|
||||
})
|
||||
}
|
||||
} else if (storageType === 'gp3') {
|
||||
// Validation rules for gp3
|
||||
const maxIOPS = Math.min(500 * totalSize, 16000)
|
||||
const maxThroughput = Math.min(0.25 * provisionedIOPS, 1000)
|
||||
|
||||
if (provisionedIOPS < 3000) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Provisioned IOPS must be at least 3000`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
} else if (provisionedIOPS > maxIOPS) {
|
||||
if (totalSize >= 8) {
|
||||
if (storageType === 'io2') {
|
||||
// Validation rules for io2
|
||||
const maxIOPS = calculateMaxIopsAllowed(totalSize)
|
||||
|
||||
if (provisionedIOPS < 100) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Provisioned IOPS must be at most ${maxIOPS}`,
|
||||
message: 'Provisioned IOPS must be at least 100',
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
} else {
|
||||
} else if (provisionedIOPS > maxIOPS) {
|
||||
if (totalSize >= 8) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Provisioned IOPS must be at most ${maxIOPS}.`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
} else {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Invalid IOPS value due to small disk size`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (throughput !== undefined) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Invalid IOPS value due to invalid disk size`,
|
||||
message: 'Throughput is not configurable for io2.',
|
||||
path: ['throughput'],
|
||||
})
|
||||
}
|
||||
|
||||
if (totalSize > DISK_LIMITS[DiskType.IO2].maxStorage) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Allocated disksize must not exceed 61,440 GB',
|
||||
path: ['totalSize'],
|
||||
})
|
||||
}
|
||||
} else if (storageType === 'gp3') {
|
||||
// Validation rules for gp3
|
||||
const maxIOPS = Math.min(500 * totalSize, 16000)
|
||||
const maxThroughput = Math.min(0.25 * provisionedIOPS, 1000)
|
||||
|
||||
if (provisionedIOPS < 3000) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Provisioned IOPS must be at least 3000`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
} else if (provisionedIOPS > maxIOPS) {
|
||||
if (totalSize >= 8) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Provisioned IOPS must be at most ${maxIOPS}`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
} else {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Invalid IOPS value due to invalid disk size`,
|
||||
path: ['provisionedIOPS'],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (throughput !== undefined && (throughput < 125 || throughput > maxThroughput)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Throughput must be set between 125 and ${maxThroughput?.toLocaleString()} MB/s.`,
|
||||
path: ['throughput'],
|
||||
})
|
||||
}
|
||||
if (totalSize > DISK_LIMITS[DiskType.GP3].maxStorage) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Allocated disksize must not exceed 16,384 GB',
|
||||
path: ['totalSize'],
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (throughput !== undefined && (throughput < 125 || throughput > maxThroughput)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: `Throughput must be set between 125 and ${maxThroughput?.toLocaleString()} MB/s.`,
|
||||
path: ['throughput'],
|
||||
})
|
||||
}
|
||||
if (totalSize > DISK_LIMITS[DiskType.GP3].maxStorage) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Allocated disksize must not exceed 16,384 GB',
|
||||
path: ['totalSize'],
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
return schema
|
||||
}
|
||||
|
||||
export type DiskStorageSchemaType = z.infer<typeof DiskStorageSchema>
|
||||
export type DiskStorageSchemaType = z.infer<ReturnType<typeof CreateDiskStorageSchema>>
|
||||
+5
-5
@@ -115,7 +115,7 @@ interface DiskSizeMeterProps {
|
||||
form: UseFormReturn<DiskStorageSchemaType>
|
||||
numReplicas: number
|
||||
isDialogOpen: boolean
|
||||
isWithinCooldown: boolean
|
||||
disabled?: boolean
|
||||
setIsDialogOpen: (isOpen: boolean) => void
|
||||
onSubmit: (values: DiskStorageSchemaType) => Promise<void>
|
||||
}
|
||||
@@ -123,7 +123,7 @@ interface DiskSizeMeterProps {
|
||||
export const DiskManagementReviewAndSubmitDialog = ({
|
||||
isDialogOpen,
|
||||
setIsDialogOpen,
|
||||
isWithinCooldown,
|
||||
disabled,
|
||||
form,
|
||||
numReplicas,
|
||||
loading,
|
||||
@@ -205,14 +205,14 @@ export const DiskManagementReviewAndSubmitDialog = ({
|
||||
const isValid = await form.trigger()
|
||||
if (isValid) setIsDialogOpen(true)
|
||||
}}
|
||||
disabled={isWithinCooldown || !isDirty}
|
||||
disabled={disabled || !isDirty}
|
||||
tooltip={{
|
||||
content: {
|
||||
side: 'bottom',
|
||||
text: !canUpdateDiskConfiguration
|
||||
? 'You need additional permissions to update disk configuration'
|
||||
: isWithinCooldown
|
||||
? 'Currently within cooldown period from previous disk change'
|
||||
: disabled
|
||||
? 'Current form values are invalid'
|
||||
: undefined,
|
||||
},
|
||||
}}
|
||||
|
||||
Reference in new issue
Block a user