diff --git a/apps/studio/components/interfaces/DiskManagement/fields/ComputeSizeField.tsx b/apps/studio/components/interfaces/DiskManagement/fields/ComputeSizeField.tsx index 0a8ecbc8d4d..e758e52d73c 100644 --- a/apps/studio/components/interfaces/DiskManagement/fields/ComputeSizeField.tsx +++ b/apps/studio/components/interfaces/DiskManagement/fields/ComputeSizeField.tsx @@ -1,9 +1,20 @@ import { SupportCategories } from '@supabase/shared-types/out/constants' import { useParams } from 'common' -import { CpuIcon, Lock, Microchip } from 'lucide-react' -import { useMemo } from 'react' +import { SupportLink } from 'components/interfaces/Support/SupportLink' +import { DocsButton } from 'components/ui/DocsButton' +import { InlineLink } from 'components/ui/InlineLink' +import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' +import { useCheckEntitlements } from 'hooks/misc/useCheckEntitlements' +import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled' +import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' +import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject' +import { getCloudProviderArchitecture } from 'lib/cloudprovider-utils' +import { DOCS_URL } from 'lib/constants' +import { ChevronRight, CpuIcon, Lock, Microchip } from 'lucide-react' +import { useEffect, useMemo, useState } from 'react' import { UseFormReturn } from 'react-hook-form' import { + Button, cn, FormField_Shadcn_, RadioGroupCard, @@ -26,16 +37,8 @@ import { import { BillingChangeBadge } from '../ui/BillingChangeBadge' import FormMessage from '../ui/FormMessage' import { NoticeBar } from '../ui/NoticeBar' -import { SupportLink } from '@/components/interfaces/Support/SupportLink' -import { DocsButton } from '@/components/ui/DocsButton' -import { InlineLink } from '@/components/ui/InlineLink' -import { useProjectAddonsQuery } from '@/data/subscriptions/project-addons-query' -import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' -import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' -import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' -import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' -import { getCloudProviderArchitecture } from '@/lib/cloudprovider-utils' -import { DOCS_URL, InstanceSpecs } from '@/lib/constants' + +const INITIALLY_VISIBLE_COUNT = 6 /** * to do: this could be a type from api-types @@ -79,6 +82,21 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) { return getAvailableComputeOptions(availableAddons, project?.cloud_provider) }, [availableAddons, project?.cloud_provider]) + // Expand by default if the project's current compute size is beyond the initial visible set + const [showAllSizes, setShowAllSizes] = useState(() => { + const idx = availableOptions.findIndex((o) => o.identifier === computeSize) + return idx >= INITIALLY_VISIBLE_COUNT + }) + + // Expand whenever the selected size falls outside the visible set — covers both initial data + // load (availableOptions starts empty) and computeSize changes after mount (e.g. form reset) + useEffect(() => { + const idx = availableOptions.findIndex((o) => o.identifier === computeSize) + if (idx >= INITIALLY_VISIBLE_COUNT) { + setShowAllSizes(true) + } + }, [computeSize, availableOptions]) + const subscriptionPitr = addons?.selected_addons.find((addon) => addon.type === 'pitr') const computeSizePrice = calculateComputeSizePrice({ @@ -91,6 +109,17 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) { const projectComputeSize = project?.infra_compute_size ?? 'nano' const showUpgradeBadge = entitledUpdateCompute && projectComputeSize === 'nano' + const selectedOptionIndex = availableOptions.findIndex((o) => o.identifier === computeSize) + const selectedOptionIsHidden = selectedOptionIndex >= INITIALLY_VISIBLE_COUNT + + // Always show all options if the selected one would be outside the visible slice, + // so the active card is never hidden from the user. + const visibleOptions = + showAllSizes || selectedOptionIsHidden + ? availableOptions + : availableOptions.slice(0, INITIALLY_VISIBLE_COUNT) + const hasHiddenOptions = availableOptions.length > INITIALLY_VISIBLE_COUNT + return ( {isLoading ? ( - Array(10) + Array(INITIALLY_VISIBLE_COUNT) .fill(0) .map((_, i) => ) ) : addonsError ? ( @@ -166,7 +195,7 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) { ) : ( <> - {availableOptions.map((compute) => { + {visibleOptions.map((compute) => { const cpuArchitecture = getCloudProviderArchitecture(project?.cloud_provider) const lockedMicroDueToPITR = @@ -205,7 +234,7 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) { key={compute.identifier} value={compute.identifier} className={cn( - 'relative text-sm text-left flex flex-col gap-0 px-0 py-3 [&_label]:w-full group] w-full h-[110px]', + 'relative text-sm text-left flex flex-col gap-0 px-0 py-3 [&_label]:w-full group w-full h-[110px]', lockedOption && 'opacity-50' )} disabled={disabled || lockedOption} @@ -293,58 +322,81 @@ export function ComputeSizeField({ form, disabled }: ComputeSizeFieldProps) { ) })} - e.preventDefault()} - className={cn( - 'relative text-sm text-left flex flex-col gap-0 px-0 py-3 [&_label]:w-full group] w-full h-[110px]' - )} - label={ - -
-
- + {showAllSizes && ( + e.preventDefault()} + className={cn( + 'relative text-sm text-left flex flex-col gap-0 px-0 py-3 [&_label]:w-full group w-full h-[110px]' + )} + label={ + +
+
+ -
- Contact Us -
-
-
-
-
- - Custom memory +
+ Contact Us
-
- - Custom CPU +
+
+
+
+ + Custom memory +
+
+ + Custom CPU +
-
- - } - /> + + } + /> + )} )}
+ + {!isLoading && !addonsError && hasHiddenOptions && ( + + )} )}