mirror of
https://github.com/supabase/supabase.git
synced 2026-10-18 15:49:09 +03:00
* init * moar * moat * moar * add read replica bar * moar * Update DiskMangementPanelForm.tsx * added temp state mang to test * moar * Prepare react queries + data from API for disk mgt * moat * moat * moar * Update DiskMangementReviewAndSubmitDialog.tsx * badge updates * Hook up actual endpoints for E2E testing, but commented out for now for local dev * Hook up real data + clean up files and add tests * Update APi types * Hook up E2E and fix all validation logic * Only show new disk mgt UI for orb billing * Add note on RRs 25% more disk size * Add state to handle free plan for disk mgt * Update increase disk size CTA in reports/database * Fix file spelling errors * Address feedback * Fix missing framer motion package in ui * Address comments * Address feedabck * Minor fix * Use new disk util endpoint * Remove unused import * Address feedback * More addressing of feedback * Update pricing details for IO2 - no included disk size --------- Co-authored-by: Jonathan Summers-Muir <MildTomato@users.noreply.github.com>
233 lines
7.1 KiB
TypeScript
233 lines
7.1 KiB
TypeScript
import { ChevronRight } from 'lucide-react'
|
|
import { UseFormReturn } from 'react-hook-form'
|
|
|
|
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
|
|
import {
|
|
Badge,
|
|
Button,
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogFooter,
|
|
DialogHeader,
|
|
DialogSectionSeparator,
|
|
DialogTitle,
|
|
DialogTrigger,
|
|
Table,
|
|
TableBody,
|
|
TableCaption,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from 'ui'
|
|
import BillingChangeBadge from './BillingChangeBadge'
|
|
import { DiskStorageSchemaType } from './DiskManagementPanelSchema'
|
|
import { DiskMangementCoolDownSection } from './DiskManagementCoolDownSection'
|
|
|
|
const TableHeaderRow = () => (
|
|
<TableRow>
|
|
<TableHead className="w-[140px] pl-5">Disk attribute</TableHead>
|
|
<TableHead className="text-right">-</TableHead>
|
|
<TableHead className="w-[100px]">Unit</TableHead>
|
|
<TableHead className="text-right pr-5">Price Change</TableHead>
|
|
</TableRow>
|
|
)
|
|
|
|
interface TableDataRowProps {
|
|
attribute: string
|
|
defaultValue: string | number
|
|
newValue: string | number
|
|
unit: string
|
|
beforePrice: number
|
|
afterPrice: number
|
|
hidePrice?: boolean
|
|
}
|
|
|
|
const TableDataRow = ({
|
|
attribute,
|
|
defaultValue,
|
|
newValue,
|
|
unit,
|
|
beforePrice,
|
|
afterPrice,
|
|
hidePrice = false,
|
|
}: TableDataRowProps) => (
|
|
<TableRow>
|
|
<TableCell className="pl-5">
|
|
<div className="flex flex-row gap-2 items-center">
|
|
<span>{attribute}</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell className="text-right font-mono">
|
|
{defaultValue !== newValue ? (
|
|
<Badge variant="default" className="bg-alternative bg-opacity-100">
|
|
<div className="flex items-center gap-1">
|
|
<span className="text-xs font-mono text-foreground-muted">{defaultValue}</span>
|
|
<ChevronRight size={12} strokeWidth={2} className="text-foreground-muted" />
|
|
<span className="text-xs font-mono text-foreground">{newValue}</span>
|
|
</div>
|
|
</Badge>
|
|
) : (
|
|
<span className="text-xs font-mono">
|
|
<span className="text-foreground-muted"></span>
|
|
{defaultValue}
|
|
</span>
|
|
)}
|
|
</TableCell>
|
|
<TableCell className="text-xs font-mono">{unit}</TableCell>
|
|
<TableCell className="text-right pr-5">
|
|
{hidePrice ? (
|
|
<span className="text-xs font-mono">-</span>
|
|
) : beforePrice !== afterPrice ? (
|
|
<BillingChangeBadge show={true} beforePrice={beforePrice} afterPrice={afterPrice} />
|
|
) : (
|
|
<span className="text-xs font-mono">${beforePrice}</span>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
)
|
|
|
|
interface DiskSizeMeterProps {
|
|
loading: boolean
|
|
form: UseFormReturn<DiskStorageSchemaType>
|
|
iopsPrice: { oldPrice: string; newPrice: string }
|
|
throughputPrice: { oldPrice: string; newPrice: string }
|
|
diskSizePrice: { oldPrice: string; newPrice: string }
|
|
isDialogOpen: boolean
|
|
isWithinCooldown: boolean
|
|
setIsDialogOpen: (isOpen: boolean) => void
|
|
onSubmit: (values: DiskStorageSchemaType) => Promise<void>
|
|
}
|
|
|
|
export const DiskManagementReviewAndSubmitDialog = ({
|
|
isDialogOpen,
|
|
setIsDialogOpen,
|
|
isWithinCooldown,
|
|
form,
|
|
loading,
|
|
onSubmit,
|
|
iopsPrice,
|
|
throughputPrice,
|
|
diskSizePrice,
|
|
}: DiskSizeMeterProps) => {
|
|
const isDirty = Object.keys(form.formState.dirtyFields).length > 0
|
|
|
|
return (
|
|
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
|
|
<DialogTrigger asChild>
|
|
<ButtonTooltip
|
|
htmlType="submit"
|
|
type="primary"
|
|
onClick={async (e) => {
|
|
e.preventDefault()
|
|
const isValid = await form.trigger()
|
|
if (isValid) setIsDialogOpen(true)
|
|
}}
|
|
disabled={isWithinCooldown || !isDirty}
|
|
tooltip={{
|
|
content: {
|
|
side: 'bottom',
|
|
text: isWithinCooldown
|
|
? 'Currently within cooldown period from previous disk change'
|
|
: undefined,
|
|
},
|
|
}}
|
|
>
|
|
Review changes
|
|
</ButtonTooltip>
|
|
</DialogTrigger>
|
|
<DialogContent size="large">
|
|
<DialogHeader>
|
|
<DialogTitle>Review changes</DialogTitle>
|
|
<DialogDescription>
|
|
Disk configuration changes will be applied shortly once confirmed.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<DialogSectionSeparator />
|
|
|
|
<Table>
|
|
<TableHeader className="font-mono uppercase text-xs [&_th]:h-auto [&_th]:pb-2 [&_th]:pt-4">
|
|
<TableHeaderRow />
|
|
</TableHeader>
|
|
<TableBody className="[&_td]:py-0 [&_tr]:h-[50px] [&_tr]:border-dotted">
|
|
<TableDataRow
|
|
hidePrice
|
|
attribute="Storage Type"
|
|
defaultValue={form.formState.defaultValues?.storageType ?? ''}
|
|
newValue={form.getValues('storageType')}
|
|
unit="-"
|
|
beforePrice={0}
|
|
afterPrice={0}
|
|
/>
|
|
<TableDataRow
|
|
attribute="Disk size"
|
|
defaultValue={form.formState.defaultValues?.totalSize ?? 0}
|
|
newValue={form.getValues('totalSize')}
|
|
unit="GiB"
|
|
beforePrice={Number(diskSizePrice.oldPrice)}
|
|
afterPrice={Number(diskSizePrice.newPrice)}
|
|
/>
|
|
<TableDataRow
|
|
attribute="IOPS"
|
|
defaultValue={form.formState.defaultValues?.provisionedIOPS ?? 0}
|
|
newValue={form.getValues('provisionedIOPS')}
|
|
unit="IOPS"
|
|
beforePrice={Number(iopsPrice.oldPrice)}
|
|
afterPrice={Number(iopsPrice.newPrice)}
|
|
/>
|
|
{form.getValues('storageType') === 'gp3' ? (
|
|
<TableDataRow
|
|
attribute="Throughput"
|
|
defaultValue={form.formState.defaultValues?.throughput ?? 0}
|
|
newValue={form.getValues('throughput') ?? 0}
|
|
unit="MiBps"
|
|
beforePrice={Number(throughputPrice.oldPrice)}
|
|
afterPrice={Number(throughputPrice.newPrice)}
|
|
/>
|
|
) : (
|
|
<TableRow>
|
|
<TableCell className="pl-5">
|
|
<div className="flex flex-row gap-2 items-center">
|
|
<span>Throughput</span>
|
|
</div>
|
|
</TableCell>
|
|
<TableCell colSpan={3}>
|
|
<p className="text-foreground-lighter text-xs text-center">
|
|
Throughput is not configurable for io2 storage type
|
|
</p>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
</TableBody>
|
|
<TableCaption className="mt-2 mb-2">
|
|
Please take note of the above billing changes
|
|
</TableCaption>
|
|
</Table>
|
|
|
|
<DialogSectionSeparator />
|
|
|
|
<DiskMangementCoolDownSection />
|
|
|
|
<DialogFooter>
|
|
<Button block size="small" type="default" onClick={() => setIsDialogOpen(false)}>
|
|
Cancel
|
|
</Button>
|
|
|
|
<Button
|
|
block
|
|
size="small"
|
|
htmlType="submit"
|
|
loading={loading}
|
|
onClick={async () => {
|
|
await onSubmit(form.getValues())
|
|
}}
|
|
>
|
|
Confirm changes
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|