From 29e107af4aa0301e1762ceddc3995d65f0f48dc1 Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Tue, 19 Aug 2025 16:36:59 +0800 Subject: [PATCH] [FE-1712] chore: use /available-regions endpoint for project creation (#37371) * chore: use /available-regions endpoint for project creation * fix types * make postgres version selection work * Small nit refactors * Feature flag changes + fix icons for flags in various places * Fix creating projects when selecting a smart region * Fix sending region selection to create project endpoint --------- Co-authored-by: Joshen Lim --- .../Database/Replication/ComingSoon.tsx | 6 +- .../interfaces/Home/ProjectUsage.tsx | 3 +- .../PostgresVersionSelector.tsx | 5 +- .../ProjectCreation/ProjectCreation.utils.ts | 10 ++ .../ProjectCreation/RegionSelector.tsx | 139 ++++++++++++++---- .../DeployNewReplicaPanel.tsx | 2 +- .../InstanceNode.tsx | 2 +- .../InfrastructureConfiguration/MapView.tsx | 13 +- .../components/ui/ComputeBadgeWrapper.tsx | 7 +- apps/studio/data/organizations/keys.ts | 2 + .../organization-available-regions-query.ts | 51 +++++++ .../data/projects/project-create-mutation.ts | 5 +- .../[slug]/deploy-button/new-project.tsx | 8 +- apps/studio/pages/new/[slug].tsx | 59 ++++++-- apps/studio/pages/project/[ref]/index.tsx | 37 ++--- .../public/img/regions/SOUTH_AFRICA.svg | 17 --- apps/studio/public/img/regions/americas.svg | 1 + ...{NORTHEAST_ASIA.svg => ap-northeast-1.svg} | 0 ...ORTHEAST_ASIA_2.svg => ap-northeast-2.svg} | 0 .../{SOUTH_ASIA.svg => ap-south-1.svg} | 0 ...{SOUTHEAST_ASIA.svg => ap-southeast-1.svg} | 0 .../{OCEANIA.svg => ap-southeast-2.svg} | 0 apps/studio/public/img/regions/apac.svg | 1 + .../{CENTRAL_CANADA.svg => ca-central-1.svg} | 0 apps/studio/public/img/regions/emea.svg | 1 + .../{CENTRAL_EU.svg => eu-central-1.svg} | 0 .../{CENTRAL_EU_2.svg => eu-central-2.svg} | 0 .../regions/{NORTH_EU.svg => eu-north-1.svg} | 0 .../regions/{WEST_EU.svg => eu-west-1.svg} | 0 .../regions/{WEST_EU_2.svg => eu-west-2.svg} | 0 .../regions/{WEST_EU_3.svg => eu-west-3.svg} | 0 .../{SOUTH_AMERICA.svg => sa-east-1.svg} | 0 .../regions/{EAST_US.svg => us-east-1.svg} | 0 .../regions/{EAST_US_2.svg => us-east-2.svg} | 0 .../regions/{WEST_US.svg => us-west-1.svg} | 0 35 files changed, 268 insertions(+), 101 deletions(-) create mode 100644 apps/studio/data/organizations/organization-available-regions-query.ts delete mode 100644 apps/studio/public/img/regions/SOUTH_AFRICA.svg create mode 100644 apps/studio/public/img/regions/americas.svg rename apps/studio/public/img/regions/{NORTHEAST_ASIA.svg => ap-northeast-1.svg} (100%) rename apps/studio/public/img/regions/{NORTHEAST_ASIA_2.svg => ap-northeast-2.svg} (100%) rename apps/studio/public/img/regions/{SOUTH_ASIA.svg => ap-south-1.svg} (100%) rename apps/studio/public/img/regions/{SOUTHEAST_ASIA.svg => ap-southeast-1.svg} (100%) rename apps/studio/public/img/regions/{OCEANIA.svg => ap-southeast-2.svg} (100%) create mode 100644 apps/studio/public/img/regions/apac.svg rename apps/studio/public/img/regions/{CENTRAL_CANADA.svg => ca-central-1.svg} (100%) create mode 100644 apps/studio/public/img/regions/emea.svg rename apps/studio/public/img/regions/{CENTRAL_EU.svg => eu-central-1.svg} (100%) rename apps/studio/public/img/regions/{CENTRAL_EU_2.svg => eu-central-2.svg} (100%) rename apps/studio/public/img/regions/{NORTH_EU.svg => eu-north-1.svg} (100%) rename apps/studio/public/img/regions/{WEST_EU.svg => eu-west-1.svg} (100%) rename apps/studio/public/img/regions/{WEST_EU_2.svg => eu-west-2.svg} (100%) rename apps/studio/public/img/regions/{WEST_EU_3.svg => eu-west-3.svg} (100%) rename apps/studio/public/img/regions/{SOUTH_AMERICA.svg => sa-east-1.svg} (100%) rename apps/studio/public/img/regions/{EAST_US.svg => us-east-1.svg} (100%) rename apps/studio/public/img/regions/{EAST_US_2.svg => us-east-2.svg} (100%) rename apps/studio/public/img/regions/{WEST_US.svg => us-west-1.svg} (100%) diff --git a/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx b/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx index d2830c5468e..4ffda79892c 100644 --- a/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx +++ b/apps/studio/components/interfaces/Database/Replication/ComingSoon.tsx @@ -20,7 +20,7 @@ const STATIC_NODES = [ label: 'Primary Database', region: 'East US (Ohio)', provider: 'AWS', - regionIcon: 'EAST_US', + regionIcon: 'us-east-1', }, position: { x: 825, y: 0 }, }, @@ -30,7 +30,7 @@ const STATIC_NODES = [ data: { label: 'Iceberg', details: '3 tables', - regionIcon: 'WEST_US', + regionIcon: 'us-west-1', }, position: { x: 875, y: 110 }, }, @@ -40,7 +40,7 @@ const STATIC_NODES = [ data: { label: 'BigQuery', details: '5 tables', - regionIcon: 'WEST_US', + regionIcon: 'us-west-1', }, position: { x: 875, y: 200 }, }, diff --git a/apps/studio/components/interfaces/Home/ProjectUsage.tsx b/apps/studio/components/interfaces/Home/ProjectUsage.tsx index 59be56f713a..1ee9a4ddd10 100644 --- a/apps/studio/components/interfaces/Home/ProjectUsage.tsx +++ b/apps/studio/components/interfaces/Home/ProjectUsage.tsx @@ -151,11 +151,10 @@ const ProjectUsage = () => { if (disabled) { const retentionDuration = LOG_RETENTION[plan?.id ?? 'free'] return ( - + diff --git a/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx b/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx index 46aeca4c5e9..1a002944dbd 100644 --- a/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx +++ b/apps/studio/components/interfaces/ProjectCreation/PostgresVersionSelector.tsx @@ -16,6 +16,7 @@ import { Select_Shadcn_, } from 'ui' import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout' +import { smartRegionToExactRegion } from './ProjectCreation.utils' interface PostgresVersionDetails { postgresEngine: PostgresEngine | undefined @@ -64,6 +65,8 @@ export const PostgresVersionSelector = ({ }: PostgresVersionSelectorProps) => { const { data: project } = useSelectedProjectQuery() + const dbRegionExact = smartRegionToExactRegion(dbRegion) + const { data: createVersions, isLoading: isLoadingProjectCreateVersions, @@ -71,7 +74,7 @@ export const PostgresVersionSelector = ({ } = useProjectCreationPostgresVersionsQuery( { cloudProvider, - dbRegion, + dbRegion: dbRegionExact, organizationSlug, }, { enabled: type === 'create' } diff --git a/apps/studio/components/interfaces/ProjectCreation/ProjectCreation.utils.ts b/apps/studio/components/interfaces/ProjectCreation/ProjectCreation.utils.ts index 9d1b56009d0..c1e11c59e66 100644 --- a/apps/studio/components/interfaces/ProjectCreation/ProjectCreation.utils.ts +++ b/apps/studio/components/interfaces/ProjectCreation/ProjectCreation.utils.ts @@ -1,6 +1,16 @@ import type { CloudProvider, Region } from 'shared-data' import { AWS_REGIONS, FLY_REGIONS } from 'shared-data' +const smartRegionToExactRegionMap = new Map([ + ['Americas', 'East US (North Virginia)'], + ['Europe', 'Central EU (Frankfurt)'], + ['APAC', 'Southeast Asia (Singapore)'], +]) + +export function smartRegionToExactRegion(smartOrExactRegion: string) { + return smartRegionToExactRegionMap.get(smartOrExactRegion) ?? smartOrExactRegion +} + export function getAvailableRegions(cloudProvider: CloudProvider): Region { switch (cloudProvider) { case 'AWS': diff --git a/apps/studio/components/interfaces/ProjectCreation/RegionSelector.tsx b/apps/studio/components/interfaces/ProjectCreation/RegionSelector.tsx index 368993359f2..2c84cfef9e9 100644 --- a/apps/studio/components/interfaces/ProjectCreation/RegionSelector.tsx +++ b/apps/studio/components/interfaces/ProjectCreation/RegionSelector.tsx @@ -1,13 +1,18 @@ -import { useRouter } from 'next/router' import { ControllerRenderProps, UseFormReturn } from 'react-hook-form' +import { useParams } from 'common' import { useDefaultRegionQuery } from 'data/misc/get-default-region-query' -import { PROVIDERS } from 'lib/constants' +import { useOrganizationAvailableRegionsQuery } from 'data/organizations/organization-available-regions-query' +import { useFlag } from 'hooks/ui/useFlag' +import { BASE_PATH, PROVIDERS } from 'lib/constants' import type { CloudProvider } from 'shared-data' import { + Badge, SelectContent_Shadcn_, SelectGroup_Shadcn_, SelectItem_Shadcn_, + SelectLabel_Shadcn_, + SelectSeparator_Shadcn_, SelectTrigger_Shadcn_, SelectValue_Shadcn_, Select_Shadcn_, @@ -31,17 +36,50 @@ export const RegionSelector = ({ field, layout = 'horizontal', }: RegionSelectorProps) => { - const router = useRouter() + const { slug } = useParams() + + const smartRegionEnabled = useFlag('enableSmartRegion') + + const { isLoading: isLoadingDefaultRegion } = useDefaultRegionQuery( + { cloudProvider }, + { enabled: !smartRegionEnabled } + ) + + const { data: availableRegionsData, isLoading: isLoadingAvailableRegions } = + useOrganizationAvailableRegionsQuery( + { + slug, + cloudProvider, + }, + { enabled: smartRegionEnabled } + ) + + const smartRegions = availableRegionsData?.all.smartGroup ?? [] + const allRegions = availableRegionsData?.all.specific ?? [] + + const recommendedSmartRegions = new Set( + [availableRegionsData?.recommendations.smartGroup.code].filter(Boolean) + ) + const recommendedSpecificRegions = new Set( + availableRegionsData?.recommendations.specific.map((region) => region.code) + ) + + const availableRegions = getAvailableRegions(PROVIDERS[cloudProvider].id) + const regionsArray = Object.entries(availableRegions).map(([key, value]) => { + return { + code: value.code, + name: value.displayName, + provider: cloudProvider, + } + }) + + const regionOptions = smartRegionEnabled ? allRegions : regionsArray + const isLoading = smartRegionEnabled ? isLoadingAvailableRegions : isLoadingDefaultRegion const showNonProdFields = process.env.NEXT_PUBLIC_ENVIRONMENT === 'local' || process.env.NEXT_PUBLIC_ENVIRONMENT === 'staging' - const availableRegions = getAvailableRegions(PROVIDERS[cloudProvider].id) - const regionsArray = Object.entries(availableRegions) - - const { isLoading: isLoadingDefaultRegion } = useDefaultRegionQuery({ cloudProvider }) - return ( } > - - - + + + + {smartRegionEnabled && ( + <> + + Smart Region Selection + {smartRegions.map((value) => { + return ( + +
+
+ region icon + {value.name} +
+ +
+ {recommendedSmartRegions.has(value.code) && ( + + Recommended + + )} +
+
+
+ ) + })} +
+ + + )} + - {regionsArray.map(([key, value]) => { - const label = value.displayName as string + All Regions + {regionOptions.map((value) => { return ( - -
- region icon - {label} + +
+
+ region icon + {value.name} +
+ +
+ {recommendedSpecificRegions.has(value.code) && ( + + Recommended + + )} +
) diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx index f4d374a3913..e70873dab20 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/DeployNewReplicaPanel.tsx @@ -443,7 +443,7 @@ const DeployNewReplicaPanel = ({ region icon )} > diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx index ef365b0604b..f6bf60e391e 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/InstanceNode.tsx @@ -141,7 +141,7 @@ export const PrimaryNode = ({ data }: NodeProps) => { region icon
{numReplicas > 0 && ( diff --git a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/MapView.tsx b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/MapView.tsx index d8486c29f07..f96afaf94b5 100644 --- a/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/MapView.tsx +++ b/apps/studio/components/interfaces/Settings/Infrastructure/InfrastructureConfiguration/MapView.tsx @@ -58,7 +58,7 @@ const MapView = ({ const [tooltip, setTooltip] = useState<{ x: number y: number - region: { key: string; country?: string; name?: string } + region: { key: string; country?: string; name?: string; region?: string } }>() const { can: canManageReplicas } = useAsyncCheckProjectPermissions( PermissionAction.CREATE, @@ -165,6 +165,7 @@ const MapView = ({ region: { key: region.key, country: region.name, + region: region.region, name: hasNoDatabases ? undefined : hasPrimary @@ -213,17 +214,17 @@ const MapView = ({
-
+
region icon -

{tooltip.region.country}

+

{tooltip.region.country}

@@ -250,7 +251,7 @@ const MapView = ({ region icon

diff --git a/apps/studio/components/ui/ComputeBadgeWrapper.tsx b/apps/studio/components/ui/ComputeBadgeWrapper.tsx index d48e3c79a0b..77515c9edb3 100644 --- a/apps/studio/components/ui/ComputeBadgeWrapper.tsx +++ b/apps/studio/components/ui/ComputeBadgeWrapper.tsx @@ -1,8 +1,8 @@ import Link from 'next/link' import { useState } from 'react' -import { components } from 'api-types' import { getAddons } from 'components/interfaces/Billing/Subscription/Subscription.utils' +import { InfraInstanceSize } from 'components/interfaces/DiskManagement/DiskManagement.types' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useProjectAddonsQuery } from 'data/subscriptions/project-addons-query' import { ProjectAddonVariantMeta } from 'data/subscriptions/types' @@ -11,7 +11,6 @@ import { INSTANCE_MICRO_SPECS } from 'lib/constants' import { Button, HoverCard, HoverCardContent, HoverCardTrigger, Separator } from 'ui' import { ComputeBadge } from 'ui-patterns/ComputeBadge' import ShimmeringLoader from './ShimmeringLoader' -import { InfraInstanceSize } from 'components/interfaces/DiskManagement/DiskManagement.types' const Row = ({ label, stat }: { label: string; stat: React.ReactNode | string }) => { return ( @@ -81,9 +80,9 @@ export const ComputeBadgeWrapper = ({ project }: ComputeBadgeWrapperProps) => { return ( setOpenState(!open)} openDelay={280}> e.stopPropagation()}> - +
- +
['organizations', slug, 'project-claim', token] as const, + availableRegions: (slug: string | undefined, cloudProvider: string) => + ['organizations', slug, 'available-regions', cloudProvider] as const, } diff --git a/apps/studio/data/organizations/organization-available-regions-query.ts b/apps/studio/data/organizations/organization-available-regions-query.ts new file mode 100644 index 00000000000..93eed9a4f0e --- /dev/null +++ b/apps/studio/data/organizations/organization-available-regions-query.ts @@ -0,0 +1,51 @@ +import { useQuery, UseQueryOptions } from '@tanstack/react-query' + +import { get, handleError } from 'data/fetchers' +import type { ResponseError } from 'types' +import { organizationKeys } from './keys' + +export type OrganizationAvailableRegionsVariables = { + slug?: string + cloudProvider: 'AWS' | 'FLY' | 'AWS_K8S' +} + +export async function getOrganizationAvailableRegions( + { slug, cloudProvider }: OrganizationAvailableRegionsVariables, + signal?: AbortSignal +) { + if (!slug) throw new Error('slug is required') + + const { data, error } = await get(`/platform/projects/available-regions`, { + params: { + query: { + cloud_provider: cloudProvider, + organization_slug: slug, + }, + }, + signal, + }) + if (error) handleError(error) + return data +} + +export type OrganizationAvailableRegionsData = Awaited< + ReturnType +> +export type OrganizationAvailableRegionsError = ResponseError + +export const useOrganizationAvailableRegionsQuery = ( + { slug, cloudProvider }: OrganizationAvailableRegionsVariables, + { + enabled = true, + ...options + }: UseQueryOptions< + OrganizationAvailableRegionsData, + OrganizationAvailableRegionsError, + TData + > = {} +) => + useQuery( + organizationKeys.availableRegions(slug, cloudProvider), + ({ signal }) => getOrganizationAvailableRegions({ slug, cloudProvider }, signal), + { enabled: enabled && typeof slug !== 'undefined', ...options } + ) diff --git a/apps/studio/data/projects/project-create-mutation.ts b/apps/studio/data/projects/project-create-mutation.ts index ecfeb2c20fc..a73b3b2c792 100644 --- a/apps/studio/data/projects/project-create-mutation.ts +++ b/apps/studio/data/projects/project-create-mutation.ts @@ -26,7 +26,8 @@ export type ProjectCreateVariables = { name: string organizationSlug: string dbPass: string - dbRegion: string + dbRegion?: string + regionSelection?: CreateProjectBody['region_selection'] dbSql?: string dbPricingTierId?: string cloudProvider?: string @@ -44,6 +45,7 @@ export async function createProject({ organizationSlug, dbPass, dbRegion, + regionSelection, dbSql, cloudProvider = PROVIDERS.AWS.id, authSiteUrl, @@ -60,6 +62,7 @@ export async function createProject({ name, db_pass: dbPass, db_region: dbRegion, + region_selection: regionSelection, db_sql: dbSql, auth_site_url: authSiteUrl, ...(customSupabaseRequest !== undefined && { diff --git a/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx b/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx index c8107f7a7b4..51a50a4b11d 100644 --- a/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx +++ b/apps/studio/pages/integrations/vercel/[slug]/deploy-button/new-project.tsx @@ -1,6 +1,5 @@ import { useParams } from 'common' import { debounce } from 'lodash' -import { useRouter } from 'next/router' import { ChangeEvent, useRef, useState } from 'react' import { toast } from 'sonner' import { Alert, Button, Checkbox, Input, Listbox } from 'ui' @@ -17,7 +16,7 @@ import { useVercelProjectsQuery } from 'data/integrations/integrations-vercel-pr import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { useProjectCreateMutation } from 'data/projects/project-create-mutation' import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization' -import { PROVIDERS } from 'lib/constants' +import { BASE_PATH, PROVIDERS } from 'lib/constants' import { getInitialMigrationSQLFromGitHubRepo } from 'lib/integration-utils' import passwordStrength from 'lib/password-strength' import { generateStrongPassword } from 'lib/project' @@ -54,7 +53,6 @@ VercelIntegration.getLayout = (page) => ( ) const CreateProject = () => { - const router = useRouter() const { data: selectedOrganization } = useSelectedOrganizationQuery() const [projectName, setProjectName] = useState('') const [dbPass, setDbPass] = useState('') @@ -258,11 +256,11 @@ const CreateProject = () => { key={option} label={label} value={label} - addOnBefore={({ active, selected }: any) => ( + addOnBefore={() => ( region icon )} > diff --git a/apps/studio/pages/new/[slug].tsx b/apps/studio/pages/new/[slug].tsx index 4af192cd973..420312bb5a8 100644 --- a/apps/studio/pages/new/[slug].tsx +++ b/apps/studio/pages/new/[slug].tsx @@ -23,6 +23,7 @@ import { PostgresVersionSelector, } from 'components/interfaces/ProjectCreation/PostgresVersionSelector' import { SPECIAL_CHARS_REGEX } from 'components/interfaces/ProjectCreation/ProjectCreation.constants' +import { smartRegionToExactRegion } from 'components/interfaces/ProjectCreation/ProjectCreation.utils' import { RegionSelector } from 'components/interfaces/ProjectCreation/RegionSelector' import { SecurityOptions } from 'components/interfaces/ProjectCreation/SecurityOptions' import { SpecialSymbolsCallout } from 'components/interfaces/ProjectCreation/SpecialSymbolsCallout' @@ -38,6 +39,7 @@ import { useOverdueInvoicesQuery } from 'data/invoices/invoices-overdue-query' import { useDefaultRegionQuery } from 'data/misc/get-default-region-query' import { useAuthorizedAppsQuery } from 'data/oauth/authorized-apps-query' import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query' +import { useOrganizationAvailableRegionsQuery } from 'data/organizations/organization-available-regions-query' import { useOrganizationsQuery } from 'data/organizations/organizations-query' import { DesiredInstanceSize, instanceSizeSpecs } from 'data/projects/new-project.constants' import { @@ -150,6 +152,7 @@ const Wizard: NextPageWithLayout = () => { const { mutate: sendEvent } = useSendEventMutation() + const smartRegionEnabled = useFlag('enableSmartRegion') const projectCreationDisabled = useFlag('disableProjectCreationAndUpdate') const showPostgresVersionSelector = useFlag('showPostgresVersionSelector') const cloudProviderEnabled = useFlag('enableFlyCloudProvider') @@ -212,23 +215,18 @@ const Wizard: NextPageWithLayout = () => { components['schemas']['ProjectInfo'][] | undefined >(undefined) - useEffect(() => { - // Only set once to ensure compute credits dont change while project is being created - if (allProjectsFromApi && !allProjects) { - setAllProjects(allProjectsFromApi) - } - }, [allProjectsFromApi, allProjects, setAllProjects]) - const organizationProjects = allProjects?.filter( (project) => project.organization_id === currentOrg?.id && project.status !== PROJECT_STATUS.INACTIVE ) ?? [] - const { data: defaultRegion, error: defaultRegionError } = useDefaultRegionQuery( + + const { data: _defaultRegion, error: defaultRegionError } = useDefaultRegionQuery( { cloudProvider: PROVIDERS[DEFAULT_PROVIDER].id, }, { + enabled: !smartRegionEnabled, refetchOnMount: false, refetchOnWindowFocus: false, refetchInterval: false, @@ -237,6 +235,26 @@ const Wizard: NextPageWithLayout = () => { } ) + const { data: availableRegionsData, error: availableRegionsError } = + useOrganizationAvailableRegionsQuery( + { + slug: slug, + cloudProvider: PROVIDERS[DEFAULT_PROVIDER].id, + }, + { + enabled: smartRegionEnabled, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchInterval: false, + refetchOnReconnect: false, + } + ) + + const regionError = smartRegionEnabled ? availableRegionsError : defaultRegionError + const defaultRegion = smartRegionEnabled + ? availableRegionsData?.recommendations.specific[0]?.name + : _defaultRegion + const isAdmin = useCheckPermissions(PermissionAction.CREATE, 'projects') const isInvalidSlug = isOrganizationsSuccess && currentOrg === undefined @@ -299,11 +317,12 @@ const Wizard: NextPageWithLayout = () => { }) const { instanceSize, cloudProvider, dbRegion, organization } = form.watch() + const dbRegionExact = smartRegionToExactRegion(dbRegion) const availableOrioleVersion = useAvailableOrioleImageVersion( { cloudProvider: cloudProvider as CloudProvider, - dbRegion, + dbRegion: smartRegionEnabled ? dbRegionExact : dbRegion, organizationSlug: organization, }, { enabled: currentOrg !== null && !isManagedByVercel } @@ -371,12 +390,16 @@ const Wizard: NextPageWithLayout = () => { const { postgresEngine, releaseChannel } = extractPostgresVersionDetails(postgresVersionSelection) + const { smartGroup = [], specific = [] } = availableRegionsData?.all ?? {} + const selectedRegion = smartRegionEnabled + ? smartGroup.find((x) => x.name === dbRegion) ?? specific.find((x) => x.name === dbRegion) + : undefined + const data: ProjectCreateVariables = { - cloudProvider: cloudProvider, + dbPass, + cloudProvider, organizationSlug: currentOrg.slug, name: projectName, - dbPass: dbPass, - dbRegion: dbRegion, // gets ignored due to org billing subscription anyway dbPricingTierId: 'tier_free', // only set the compute size on pro+ plans. Free plans always use micro (nano in the future) size. @@ -385,6 +408,7 @@ const Wizard: NextPageWithLayout = () => { dataApiUseApiSchema: !dataApi ? false : useApiSchema, postgresEngine: useOrioleDb ? availableOrioleVersion?.postgres_engine : postgresEngine, releaseChannel: useOrioleDb ? availableOrioleVersion?.release_channel : releaseChannel, + ...(smartRegionEnabled ? { regionSelection: selectedRegion } : { dbRegion }), } if (postgresVersion) { @@ -402,6 +426,13 @@ const Wizard: NextPageWithLayout = () => { createProject(data) } + useEffect(() => { + // Only set once to ensure compute credits dont change while project is being created + if (allProjectsFromApi && !allProjects) { + setAllProjects(allProjectsFromApi) + } + }, [allProjectsFromApi, allProjects, setAllProjects]) + useEffect(() => { // Handle no org: redirect to new org route if (isEmptyOrganizations) { @@ -423,10 +454,10 @@ const Wizard: NextPageWithLayout = () => { }, [defaultRegion]) useEffect(() => { - if (defaultRegionError) { + if (regionError) { form.setValue('dbRegion', PROVIDERS[DEFAULT_PROVIDER].default_region.displayName) } - }, [defaultRegionError]) + }, [regionError]) const availableComputeCredits = organizationProjects.length === 0 ? 10 : 0 diff --git a/apps/studio/pages/project/[ref]/index.tsx b/apps/studio/pages/project/[ref]/index.tsx index c8ca8cfcce9..f075fd2b228 100644 --- a/apps/studio/pages/project/[ref]/index.tsx +++ b/apps/studio/pages/project/[ref]/index.tsx @@ -166,13 +166,12 @@ const Home: NextPageWithLayout = () => { > Tables -

- {isLoadingTables ? ( - - ) : ( - tablesCount - )} -

+ + {isLoadingTables ? ( + + ) : ( +

{tablesCount}

+ )}
{IS_PLATFORM && ( @@ -183,13 +182,11 @@ const Home: NextPageWithLayout = () => { > Functions -

- {isLoadingFunctions ? ( - - ) : ( - functionsCount - )} -

+ {isLoadingFunctions ? ( + + ) : ( +

{functionsCount}

+ )}
)} @@ -201,13 +198,11 @@ const Home: NextPageWithLayout = () => { > Replicas -

- {isLoadingReplicas ? ( - - ) : ( - replicasCount - )} -

+ {isLoadingReplicas ? ( + + ) : ( +

{replicasCount}

+ )} )} diff --git a/apps/studio/public/img/regions/SOUTH_AFRICA.svg b/apps/studio/public/img/regions/SOUTH_AFRICA.svg deleted file mode 100644 index 1e0b8b23bb1..00000000000 --- a/apps/studio/public/img/regions/SOUTH_AFRICA.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/apps/studio/public/img/regions/americas.svg b/apps/studio/public/img/regions/americas.svg new file mode 100644 index 00000000000..d8c679a536b --- /dev/null +++ b/apps/studio/public/img/regions/americas.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/studio/public/img/regions/NORTHEAST_ASIA.svg b/apps/studio/public/img/regions/ap-northeast-1.svg similarity index 100% rename from apps/studio/public/img/regions/NORTHEAST_ASIA.svg rename to apps/studio/public/img/regions/ap-northeast-1.svg diff --git a/apps/studio/public/img/regions/NORTHEAST_ASIA_2.svg b/apps/studio/public/img/regions/ap-northeast-2.svg similarity index 100% rename from apps/studio/public/img/regions/NORTHEAST_ASIA_2.svg rename to apps/studio/public/img/regions/ap-northeast-2.svg diff --git a/apps/studio/public/img/regions/SOUTH_ASIA.svg b/apps/studio/public/img/regions/ap-south-1.svg similarity index 100% rename from apps/studio/public/img/regions/SOUTH_ASIA.svg rename to apps/studio/public/img/regions/ap-south-1.svg diff --git a/apps/studio/public/img/regions/SOUTHEAST_ASIA.svg b/apps/studio/public/img/regions/ap-southeast-1.svg similarity index 100% rename from apps/studio/public/img/regions/SOUTHEAST_ASIA.svg rename to apps/studio/public/img/regions/ap-southeast-1.svg diff --git a/apps/studio/public/img/regions/OCEANIA.svg b/apps/studio/public/img/regions/ap-southeast-2.svg similarity index 100% rename from apps/studio/public/img/regions/OCEANIA.svg rename to apps/studio/public/img/regions/ap-southeast-2.svg diff --git a/apps/studio/public/img/regions/apac.svg b/apps/studio/public/img/regions/apac.svg new file mode 100644 index 00000000000..786803e32c9 --- /dev/null +++ b/apps/studio/public/img/regions/apac.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/studio/public/img/regions/CENTRAL_CANADA.svg b/apps/studio/public/img/regions/ca-central-1.svg similarity index 100% rename from apps/studio/public/img/regions/CENTRAL_CANADA.svg rename to apps/studio/public/img/regions/ca-central-1.svg diff --git a/apps/studio/public/img/regions/emea.svg b/apps/studio/public/img/regions/emea.svg new file mode 100644 index 00000000000..f9ff9207d6e --- /dev/null +++ b/apps/studio/public/img/regions/emea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/studio/public/img/regions/CENTRAL_EU.svg b/apps/studio/public/img/regions/eu-central-1.svg similarity index 100% rename from apps/studio/public/img/regions/CENTRAL_EU.svg rename to apps/studio/public/img/regions/eu-central-1.svg diff --git a/apps/studio/public/img/regions/CENTRAL_EU_2.svg b/apps/studio/public/img/regions/eu-central-2.svg similarity index 100% rename from apps/studio/public/img/regions/CENTRAL_EU_2.svg rename to apps/studio/public/img/regions/eu-central-2.svg diff --git a/apps/studio/public/img/regions/NORTH_EU.svg b/apps/studio/public/img/regions/eu-north-1.svg similarity index 100% rename from apps/studio/public/img/regions/NORTH_EU.svg rename to apps/studio/public/img/regions/eu-north-1.svg diff --git a/apps/studio/public/img/regions/WEST_EU.svg b/apps/studio/public/img/regions/eu-west-1.svg similarity index 100% rename from apps/studio/public/img/regions/WEST_EU.svg rename to apps/studio/public/img/regions/eu-west-1.svg diff --git a/apps/studio/public/img/regions/WEST_EU_2.svg b/apps/studio/public/img/regions/eu-west-2.svg similarity index 100% rename from apps/studio/public/img/regions/WEST_EU_2.svg rename to apps/studio/public/img/regions/eu-west-2.svg diff --git a/apps/studio/public/img/regions/WEST_EU_3.svg b/apps/studio/public/img/regions/eu-west-3.svg similarity index 100% rename from apps/studio/public/img/regions/WEST_EU_3.svg rename to apps/studio/public/img/regions/eu-west-3.svg diff --git a/apps/studio/public/img/regions/SOUTH_AMERICA.svg b/apps/studio/public/img/regions/sa-east-1.svg similarity index 100% rename from apps/studio/public/img/regions/SOUTH_AMERICA.svg rename to apps/studio/public/img/regions/sa-east-1.svg diff --git a/apps/studio/public/img/regions/EAST_US.svg b/apps/studio/public/img/regions/us-east-1.svg similarity index 100% rename from apps/studio/public/img/regions/EAST_US.svg rename to apps/studio/public/img/regions/us-east-1.svg diff --git a/apps/studio/public/img/regions/EAST_US_2.svg b/apps/studio/public/img/regions/us-east-2.svg similarity index 100% rename from apps/studio/public/img/regions/EAST_US_2.svg rename to apps/studio/public/img/regions/us-east-2.svg diff --git a/apps/studio/public/img/regions/WEST_US.svg b/apps/studio/public/img/regions/us-west-1.svg similarity index 100% rename from apps/studio/public/img/regions/WEST_US.svg rename to apps/studio/public/img/regions/us-west-1.svg