diff --git a/packages/api-v4/src/quotas/types.ts b/packages/api-v4/src/quotas/types.ts index 080b2da9e21..4819a243afa 100644 --- a/packages/api-v4/src/quotas/types.ts +++ b/packages/api-v4/src/quotas/types.ts @@ -78,8 +78,10 @@ export interface QuotaUsage { /** * The current account usage, measured in units specified by the * `resource_metric` field. + * + * This can be null if the user does not have resources for the given Quota Name. */ - used: number; + used: number | null; } export const quotaTypes = { diff --git a/packages/manager/src/features/Account/Quotas/Quotas.tsx b/packages/manager/src/features/Account/Quotas/Quotas.tsx index ee6dec445bf..f7767f9add0 100644 --- a/packages/manager/src/features/Account/Quotas/Quotas.tsx +++ b/packages/manager/src/features/Account/Quotas/Quotas.tsx @@ -13,7 +13,6 @@ import * as React from 'react'; import { DocsLink } from 'src/components/DocsLink/DocsLink'; import { DocumentTitleSegment } from 'src/components/DocumentTitle'; import { RegionSelect } from 'src/components/RegionSelect/RegionSelect'; -import { regionFactory } from 'src/factories'; import { useQuotasQuery } from 'src/queries/quotas/quotas'; import { useGetLocationsForQuotaService } from './utils'; @@ -60,19 +59,10 @@ export const Quotas = () => { value: key as QuotaType, })); - // Build Location options - + // Destructure Locations for both the regions and s3 endpoints controls const { regions, s3Endpoints } = locationData; - const globalOption = regionFactory.build({ - capabilities: [], - id: 'global', - label: 'Global (Account level)', - }); - const memoizedLocationOptions = React.useMemo(() => { - return [globalOption, ...(regions ?? [])]; - }, [regions, globalOption]); - // Fetch the usage for each quota + // Fetch the usage for each quota, depending on the service const quotaIds = quotas?.data.map((quota) => quota.quota_id) ?? []; const quotaUsageQueries = useQueries({ queries: quotaIds.map((quotaId) => ({ @@ -109,7 +99,6 @@ export const Quotas = () => { ) => { setSelectedService(value); setSelectedLocation(null); - refetch(); }; return ( @@ -138,11 +127,12 @@ export const Quotas = () => { label: value?.label, value: value?.value, }); + refetch(); }} options={ - memoizedLocationOptions.map((location) => ({ + s3Endpoints?.map((location) => ({ label: location.label, - value: location.label, + value: location.value, })) ?? [] } placeholder={ @@ -153,7 +143,8 @@ export const Quotas = () => { value={{ label: s3Endpoints?.find( - (loc) => loc.label === selectedLocation?.value + (s3Endpoint) => + s3Endpoint.value === selectedLocation?.value )?.label ?? '', value: selectedLocation?.value ?? '', }} @@ -169,6 +160,7 @@ export const Quotas = () => { label: value.label, value: value.id, }); + refetch(); }} placeholder={ isFetchingLocations @@ -180,7 +172,7 @@ export const Quotas = () => { disabled={isFetchingLocations} loading={isFetchingLocations} noOptionsText={`No resource found for ${selectedService.label}`} - regions={memoizedLocationOptions} + regions={regions ?? []} sx={{ flexGrow: 1, mr: 2 }} value={selectedLocation?.value} /> @@ -201,7 +193,7 @@ export const Quotas = () => {
= 2 - ? [{ label: 'Global (Account level)', value: 'global' }] - : []), + ...[{ label: 'Global (Account level)', value: 'global' }], ...uniqueEndpoints.map((endpoint) => ({ label: `${endpoint.endpoint} (Standard ${endpoint.endpoint_type})`, value: endpoint.endpoint, diff --git a/packages/manager/src/mocks/presets/crud/handlers/quotas.ts b/packages/manager/src/mocks/presets/crud/handlers/quotas.ts index 276cf1c075b..c9725c57a48 100644 --- a/packages/manager/src/mocks/presets/crud/handlers/quotas.ts +++ b/packages/manager/src/mocks/presets/crud/handlers/quotas.ts @@ -7,7 +7,7 @@ import { makePaginatedResponse, makeResponse, } from 'src/mocks/utilities/response'; -import { pickRandom, pickXRandomFromArray } from 'src/utilities/random'; +import { pickRandom } from 'src/utilities/random'; import type { Quota, QuotaType, QuotaUsage } from '@linode/api-v4'; import type { StrictResponse } from 'msw'; @@ -18,7 +18,7 @@ import type { const mockQuotas: Record= { linode: [ - ...pickXRandomFromArray(regions, 3).map((region) => + ...regions.map((region) => quotaFactory.build({ description: 'Max number of vCPUs assigned to Linodes with Dedicated plans', @@ -28,7 +28,7 @@ const mockQuotas: Record = { resource_metric: 'CPU', }) ), - ...pickXRandomFromArray(regions, 3).map((region) => + ...regions.map((region) => quotaFactory.build({ description: 'Max number of vCPUs assigned to Linodes with Shared plans', @@ -38,7 +38,7 @@ const mockQuotas: Record = { resource_metric: 'CPU', }) ), - ...pickXRandomFromArray(regions, 3).map((region) => + ...regions.map((region) => quotaFactory.build({ description: 'Max number of GPUs assigned to Linodes with GPU plans', quota_limit: pickRandom([5, 6, 7, 8, 9, 10]), @@ -47,7 +47,7 @@ const mockQuotas: Record = { resource_metric: 'GPU', }) ), - ...pickXRandomFromArray(regions, 3).map((region) => + ...regions.map((region) => quotaFactory.build({ description: 'Max number of VPUs assigned to Linodes with VPU plans', quota_limit: pickRandom([10, 20, 30, 40, 50]), @@ -56,7 +56,7 @@ const mockQuotas: Record = { resource_metric: 'VPU', }) ), - ...pickXRandomFromArray(regions, 3).map((region) => + ...regions.map((region) => quotaFactory.build({ description: 'Max number of vCPUs assigned to Linodes with High Memory plans', @@ -68,7 +68,7 @@ const mockQuotas: Record = { ), ], lke: [ - ...pickXRandomFromArray(regions, 3).map((region) => + ...regions.map((region) => quotaFactory.build({ quota_limit: pickRandom([10, 20, 30, 40, 50]), quota_name: 'Total number of Clusters', @@ -156,14 +156,14 @@ export const getQuotas = () => [ return makeResponse( quotaUsageFactory.build({ quota_limit: quota.quota_limit, - used: pickRandom([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]), + used: pickRandom([5, 6, 7, 8, 9, 10, null]), }) ); case 'lke': return makeResponse( quotaUsageFactory.build({ quota_limit: quota.quota_limit, - used: pickRandom([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]), + used: pickRandom([0, 1, 2, 3, 4, 5, null]), }) ); case 'object-storage': @@ -178,8 +178,9 @@ export const getQuotas = () => [ 200_000_000, 300_000_000, 400_000_000, + null, ]) - : pickRandom([100, 200, 300, 400, 500]), + : pickRandom([100, 200, 300, 400, 500, null]), }) ); default: diff --git a/packages/manager/src/utilities/random.ts b/packages/manager/src/utilities/random.ts index 98cf5136502..f21149ad513 100644 --- a/packages/manager/src/utilities/random.ts +++ b/packages/manager/src/utilities/random.ts @@ -7,16 +7,6 @@ export const pickRandom = (items: T[]): T => { return items[Math.floor(Math.random() * items.length)]; }; -/** - * Picks random elements from an array - * @param items { T[] } an array of any kind - * @param count { number } the number of elements to pick - * @returns {T[]} an array of the given type - */ -export const pickXRandomFromArray = (items: T[], count: number): T[] => { - return items.sort(() => Math.random() - 0.5).slice(0, count); -}; - /** * Generates a random date between two dates * @param start {Date} the start date