Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ import { IOPSField } from './fields/IOPSField'
import { StorageTypeField } from './fields/StorageTypeField'
import { ThroughputField } from './fields/ThroughputField'
import { DiskCountdownRadial } from './ui/DiskCountdownRadial'
import { DiskType, RESTRICTED_COMPUTE_FOR_THROUGHPUT_ON_GP3 } from './ui/DiskManagement.constants'
import {
DISK_LIMITS,
DiskType,
RESTRICTED_COMPUTE_FOR_THROUGHPUT_ON_GP3,
} from './ui/DiskManagement.constants'
import { NoticeBar } from './ui/NoticeBar'
import { SpendCapDisabledSection } from './ui/SpendCapDisabledSection'
import { useIsAwsCloudProvider, useIsAwsK8sCloudProvider } from 'hooks/misc/useSelectedProject'
Expand Down Expand Up @@ -142,6 +146,10 @@ export function DiskManagementForm() {
/**
* Handle default values
*/
const computeSize = project?.infra_compute_size
? mapComputeSizeNameToAddonVariantId(project?.infra_compute_size)
: undefined

// @ts-ignore
const { type, iops, throughput_mbps, size_gb } = data?.attributes ?? { size_gb: 0, iops: 0 }
const { growth_percent, max_size_gb, min_increment_gb } = diskAutoscaleConfig ?? {}
Expand All @@ -150,9 +158,7 @@ export function DiskManagementForm() {
provisionedIOPS: iops,
throughput: throughput_mbps,
totalSize: size_gb,
computeSize: project?.infra_compute_size
? mapComputeSizeNameToAddonVariantId(project?.infra_compute_size)
: undefined,
computeSize,
growthPercent: growth_percent,
minIncrementGb: min_increment_gb,
maxSizeGb: max_size_gb,
Expand All @@ -167,6 +173,20 @@ export function DiskManagementForm() {
reValidateMode: 'onChange',
})

const { computeSize: modifiedComputeSize } = form.watch()

// We only support disk configurations for >=Large instances
// If a customer downgrades back to <Large, we should reset the storage settings to avoid incurring unnecessary costs
useEffect(() => {
if (modifiedComputeSize && project?.infra_compute_size && isDialogOpen) {
if (RESTRICTED_COMPUTE_FOR_THROUGHPUT_ON_GP3.includes(modifiedComputeSize)) {
form.setValue('storageType', DiskType.GP3)
form.setValue('throughput', DISK_LIMITS['gp3'].minThroughput)
form.setValue('provisionedIOPS', DISK_LIMITS['gp3'].minIops)
}
}
}, [modifiedComputeSize, isDialogOpen, project])

/**
* State handling
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ import {
} from '../DiskManagement.utils'
import { BillingChangeBadge } from '../ui/BillingChangeBadge'
import { ComputeSizeRecommendationSection } from '../ui/ComputeSizeRecommendationSection'
import {
COMPUTE_BASELINE_IOPS,
DiskType,
RESTRICTED_COMPUTE_FOR_IOPS_ON_GP3,
} from '../ui/DiskManagement.constants'
import { DiskType, RESTRICTED_COMPUTE_FOR_IOPS_ON_GP3 } from '../ui/DiskManagement.constants'
import { DiskManagementIOPSReadReplicas } from '../ui/DiskManagementReadReplicas'
import FormMessage from '../ui/FormMessage'
import { InputPostTab } from '../ui/InputPostTab'
Expand Down Expand Up @@ -117,13 +113,7 @@ export function IOPSField({ form, disableInput }: IOPSFieldProps) {
type="number"
className="flex-grow font-mono rounded-r-none max-w-32"
{...field}
value={
disableIopsInput
? COMPUTE_BASELINE_IOPS[
watchedComputeSize as keyof typeof COMPUTE_BASELINE_IOPS
]
: field.value
}
value={field.value}
disabled={disableInput || disableIopsInput || isError}
onChange={(e) => {
setValue('provisionedIOPS', e.target.valueAsNumber, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { DiskStorageSchemaType } from '../DiskManagement.schema'
import { calculateThroughputPrice } from '../DiskManagement.utils'
import { BillingChangeBadge } from '../ui/BillingChangeBadge'
import {
COMPUTE_BASELINE_THROUGHPUT,
DISK_LIMITS,
DiskType,
RESTRICTED_COMPUTE_FOR_IOPS_ON_GP3,
Expand Down Expand Up @@ -129,13 +128,7 @@ export function ThroughputField({ form, disableInput }: ThroughputFieldProps) {
<Input_Shadcn_
type="number"
{...field}
value={
disableIopsInput
? COMPUTE_BASELINE_THROUGHPUT[
watchedComputeSize as keyof typeof COMPUTE_BASELINE_THROUGHPUT
]
: field.value
}
value={field.value}
onChange={(e) => {
setValue('throughput', e.target.valueAsNumber, {
shouldDirty: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import z from 'zod'
import { useParams } from 'common'
import Panel from 'components/ui/Panel'
import { useSecretsCreateMutation } from 'data/secrets/secrets-create-mutation'
import { useSecretsQuery } from 'data/secrets/secrets-query'
import { Eye, EyeOff, MinusCircle } from 'lucide-react'
import { DuplicateSecretWarningModal } from './DuplicateSecretWarningModal'
import {
Button,
Form_Shadcn_,
Expand Down Expand Up @@ -44,6 +46,8 @@ const defaultValues = {
const AddNewSecretForm = () => {
const { ref: projectRef } = useParams()
const [showSecretValue, setShowSecretValue] = useState(false)
const [duplicateSecretName, setDuplicateSecretName] = useState<string>('')
const [pendingSecrets, setPendingSecrets] = useState<z.infer<typeof FormSchema> | null>(null)

const form = useForm({
resolver: zodResolver(FormSchema),
Expand All @@ -55,6 +59,10 @@ const AddNewSecretForm = () => {
name: 'secrets',
})

const { data: existingSecrets } = useSecretsQuery({
projectRef: projectRef,
})

function handlePaste(e: ClipboardEvent) {
e.preventDefault()
const text = e.clipboardData?.getData('text')
Expand Down Expand Up @@ -114,9 +122,32 @@ const AddNewSecretForm = () => {
})

const onSubmit: SubmitHandler<z.infer<typeof FormSchema>> = async (data) => {
// Check for duplicate secret names
const existingSecretNames = existingSecrets?.map((secret) => secret.name) || []
const duplicateSecret = data.secrets.find((secret) => existingSecretNames.includes(secret.name))

if (duplicateSecret) {
setDuplicateSecretName(duplicateSecret.name)
setPendingSecrets(data)
return
}

createSecret({ projectRef, secrets: data.secrets })
}

const handleConfirmDuplicate = () => {
if (pendingSecrets) {
createSecret({ projectRef, secrets: pendingSecrets.secrets })
setDuplicateSecretName('')
setPendingSecrets(null)
}
}

const handleCancelDuplicate = () => {
setDuplicateSecretName('')
setPendingSecrets(null)
}

return (
<Panel>
<Panel.Content className="grid gap-4">
Expand Down Expand Up @@ -202,6 +233,14 @@ const AddNewSecretForm = () => {
</form>
</Form_Shadcn_>
</Panel.Content>

<DuplicateSecretWarningModal
visible={!!duplicateSecretName}
onCancel={handleCancelDuplicate}
onConfirm={handleConfirmDuplicate}
isCreating={isCreating}
secretName={duplicateSecretName}
/>
</Panel>
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ConfirmationModal from 'ui-patterns/Dialogs/ConfirmationModal'

interface DuplicateSecretWarningModalProps {
visible: boolean
onCancel: () => void
onConfirm: () => void
isCreating: boolean
secretName: string
}

export const DuplicateSecretWarningModal = ({
visible,
onCancel,
onConfirm,
isCreating,
secretName,
}: DuplicateSecretWarningModalProps) => {
return (
<ConfirmationModal
visible={visible}
size="medium"
title="Confirm replacing existing secret"
confirmLabel="Replace secret"
confirmLabelLoading="Replacing secret"
variant="warning"
loading={isCreating}
onCancel={onCancel}
onConfirm={onConfirm}
>
<p className="text-sm text-foreground-light">
A secret with the name "{secretName}" already exists. Continuing will replace the existing
secret with the new value. This action cannot be undone. Are you sure you want to proceed?
</p>
</ConfirmationModal>
)
}
Loading