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