fix: update storage size unit correctly after navigation (#48932)

Fixes FE-4128. 

## What is the current behavior?

When updating the global Storage file size limit using a unit other than
MB, the selected unit displays an incorrect value after navigating away
from the Storage settings page and returning.

The updated file size is persisted correctly by the API, but the unit
selector does not always reflect the value derived from the persisted
configuration.

The Save button also remains enabled after successfully saving the
updated configuration.

## What is the new behavior?

The file size unit selector now correctly reflects the unit derived from
the persisted global file size limit after saving and navigating between
pages.

The form state is also correctly synchronized with the latest Storage
configuration after an update, so the Save button returns to its
disabled state once the changes have been persisted.

## Additional context

The Storage API persists the global file size limit in bytes rather than
persisting the selected display unit separately. The dashboard derives
the appropriate unit (MB/GB) from the stored byte value when loading the
configuration.

The issue was caused by the unit Select retaining stale internal state
when the form values were reset after the Storage configuration was
loaded/refetched. Ensuring the Select is refreshed when the controlled
unit changes keeps the displayed unit synchronized with the form state.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved storage settings form initialization when configuration and
entitlements load.
* Ensured storage unit selections and placeholders display consistently.
  * Improved form resetting to reflect the latest loaded settings.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Monica Khoury authored and GitHub committed 2026-08-11 07:17:06 -06:00
1 parent 5b301e1ffa
commit 0f2f3e4eb7
1 file changed
+17 -36
@@ -2,7 +2,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { PermissionAction } from '@supabase/shared-types/out/constants'
import { IS_PLATFORM, useParams } from 'common'
import { useEffect, useMemo, useState } from 'react'
import { SubmitHandler, useForm, useWatch } from 'react-hook-form'
import { SubmitHandler, useForm } from 'react-hook-form'
import { toast } from 'sonner'
import {
Button,
@@ -56,12 +56,6 @@ import { formatBytes } from '@/lib/helpers'
const formId = 'storage-settings-form'
interface StorageSettingsState {
fileSizeLimit: number
unit: StorageSizeUnits
imageTransformationEnabled: boolean
}
export const StorageSettings = () => {
const { ref: projectRef } = useParams()
const { data: project } = useSelectedProjectQuery()
@@ -112,11 +106,6 @@ export const StorageSettings = () => {
!hasAccessToImageTransformations && !hasAccessToFileSizeConfiguration
const [isUpdating, setIsUpdating] = useState(false)
const [initialValues, setInitialValues] = useState<StorageSettingsState>({
fileSizeLimit: 0,
unit: StorageSizeUnits.MB,
imageTransformationEnabled: false,
})
const maxBytes = useMemo(() => {
if (organization?.usage_billing_enabled || isEntitlementUnlimited()) {
@@ -153,12 +142,15 @@ export const StorageSettings = () => {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
defaultValues: initialValues,
defaultValues: {
fileSizeLimit: 0,
unit: StorageSizeUnits.MB,
imageTransformationEnabled: false,
},
mode: 'onSubmit',
reValidateMode: 'onSubmit',
})
const storageUnit = useWatch({ control: form.control, name: 'unit' })
const fileSizeLimitError = form.formState.errors.fileSizeLimit
const { mutate: updateStorageConfig } = useProjectStorageConfigUpdateUpdateMutation({
@@ -225,27 +217,17 @@ export const StorageSettings = () => {
}
useEffect(() => {
if (isSuccess && config && !isLoading) {
const { fileSizeLimit, features } = config
const { value, unit } = convertFromBytes(fileSizeLimit ?? 0)
const imageTransformationEnabled =
features?.imageTransformation?.enabled ?? hasAccessToImageTransformations
if (!isSuccess || !config || isLoading) return
setInitialValues({
fileSizeLimit: value,
unit: unit,
imageTransformationEnabled,
})
const { value, unit } = convertFromBytes(config.fileSizeLimit ?? 0)
// Reset the form values when the config values load
form.reset({
fileSizeLimit: value,
unit: unit,
imageTransformationEnabled,
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSuccess, config, isLoading, hasAccessToImageTransformations])
form.reset({
fileSizeLimit: value,
unit,
imageTransformationEnabled:
config.features?.imageTransformation?.enabled ?? hasAccessToImageTransformations,
})
}, [isSuccess, config, isLoading, hasAccessToImageTransformations, form])
return (
<PageContainer>
@@ -355,6 +337,7 @@ export const StorageSettings = () => {
name="unit"
render={({ field: unitField }) => (
<Select
key={unitField.value}
value={unitField.value}
onValueChange={(val) => {
unitField.onChange(val)
@@ -366,9 +349,7 @@ export const StorageSettings = () => {
}
>
<SelectTrigger className="w-[90px] text-xs font-mono rounded-l-none bg-surface-300">
<SelectValue placeholder="Choose a prefix">
{storageUnit}
</SelectValue>
<SelectValue placeholder="Choose a prefix" />
</SelectTrigger>
<SelectContent>
{Object.values(StorageSizeUnits).map((unit: string) => (