mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
[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 <joshenlimek@gmail.com>
This commit is contained in:
1 parent
a0f86b3010
commit
29e107af4a
35 files changed
+268
-101
No files matched your search
@@ -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 },
|
||||
},
|
||||
|
||||
@@ -151,11 +151,10 @@ const ProjectUsage = () => {
|
||||
if (disabled) {
|
||||
const retentionDuration = LOG_RETENTION[plan?.id ?? 'free']
|
||||
return (
|
||||
<Tooltip>
|
||||
<Tooltip key={i.key}>
|
||||
<TooltipTrigger asChild>
|
||||
<DropdownMenuRadioItem
|
||||
disabled
|
||||
key={i.key}
|
||||
value={i.key}
|
||||
className="!pointer-events-auto"
|
||||
>
|
||||
|
||||
@@ -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' }
|
||||
|
||||
@@ -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':
|
||||
|
||||
@@ -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 (
|
||||
<FormItemLayout
|
||||
layout={layout}
|
||||
@@ -57,27 +95,78 @@ export const RegionSelector = ({
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Select_Shadcn_
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
disabled={isLoadingDefaultRegion}
|
||||
>
|
||||
<SelectTrigger_Shadcn_>
|
||||
<SelectValue_Shadcn_ placeholder="Select a region for your project.." />
|
||||
<Select_Shadcn_ value={field.value} onValueChange={field.onChange} disabled={isLoading}>
|
||||
<SelectTrigger_Shadcn_ className="[&>:nth-child(1)]:w-full [&>:nth-child(1)]:flex [&>:nth-child(1)]:items-start">
|
||||
<SelectValue_Shadcn_
|
||||
placeholder={
|
||||
isLoading ? 'Loading available regions...' : 'Select a region for your project..'
|
||||
}
|
||||
/>
|
||||
</SelectTrigger_Shadcn_>
|
||||
<SelectContent_Shadcn_>
|
||||
{smartRegionEnabled && (
|
||||
<>
|
||||
<SelectGroup_Shadcn_>
|
||||
<SelectLabel_Shadcn_>Smart Region Selection</SelectLabel_Shadcn_>
|
||||
{smartRegions.map((value) => {
|
||||
return (
|
||||
<SelectItem_Shadcn_
|
||||
key={value.code}
|
||||
value={value.name}
|
||||
className="w-full [&>:nth-child(2)]:w-full"
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full">
|
||||
<div className="flex items-center gap-x-3">
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-5 rounded-sm"
|
||||
src={`${BASE_PATH}/img/regions/${value.code}.svg`}
|
||||
/>
|
||||
<span className="text-foreground">{value.name}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{recommendedSmartRegions.has(value.code) && (
|
||||
<Badge variant="success" className="mr-1">
|
||||
Recommended
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem_Shadcn_>
|
||||
)
|
||||
})}
|
||||
</SelectGroup_Shadcn_>
|
||||
<SelectSeparator_Shadcn_ />
|
||||
</>
|
||||
)}
|
||||
|
||||
<SelectGroup_Shadcn_>
|
||||
{regionsArray.map(([key, value]) => {
|
||||
const label = value.displayName as string
|
||||
<SelectLabel_Shadcn_>All Regions</SelectLabel_Shadcn_>
|
||||
{regionOptions.map((value) => {
|
||||
return (
|
||||
<SelectItem_Shadcn_ key={key} value={label}>
|
||||
<div className="flex items-center gap-3">
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-5 rounded-sm"
|
||||
src={`${router.basePath}/img/regions/${key}.svg`}
|
||||
/>
|
||||
<span className="text-foreground">{label}</span>
|
||||
<SelectItem_Shadcn_
|
||||
key={value.code}
|
||||
value={value.name}
|
||||
className="w-full [&>:nth-child(2)]:w-full"
|
||||
>
|
||||
<div className="flex flex-row items-center justify-between w-full">
|
||||
<div className="flex items-center gap-x-3">
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-5 rounded-sm"
|
||||
src={`${BASE_PATH}/img/regions/${value.code}.svg`}
|
||||
/>
|
||||
<span className="text-foreground">{value.name}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{recommendedSpecificRegions.has(value.code) && (
|
||||
<Badge variant="success" className="mr-1">
|
||||
Recommended
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</SelectItem_Shadcn_>
|
||||
)
|
||||
|
||||
+1
-1
@@ -443,7 +443,7 @@ const DeployNewReplicaPanel = ({
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-5 rounded-sm"
|
||||
src={`${BASE_PATH}/img/regions/${region.key}.svg`}
|
||||
src={`${BASE_PATH}/img/regions/${region.region}.svg`}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
|
||||
+1
-1
@@ -141,7 +141,7 @@ export const PrimaryNode = ({ data }: NodeProps<PrimaryNodeData>) => {
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-8 rounded-sm mt-0.5"
|
||||
src={`${BASE_PATH}/img/regions/${region.key}.svg`}
|
||||
src={`${BASE_PATH}/img/regions/${region.region}.svg`}
|
||||
/>
|
||||
</div>
|
||||
{numReplicas > 0 && (
|
||||
|
||||
+7
-6
@@ -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 = ({
|
||||
<Marker coordinates={[tooltip.x - 47, tooltip.y - 5]}>
|
||||
<foreignObject width={220} height={66.25}>
|
||||
<div className="bg-studio/50 rounded border">
|
||||
<div className="px-3 py-2 flex flex-col gap-y-1">
|
||||
<div className="px-3 py-2 flex flex-col">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-4 rounded-sm"
|
||||
src={`${BASE_PATH}/img/regions/${tooltip.region.key}.svg`}
|
||||
src={`${BASE_PATH}/img/regions/${tooltip.region.region}.svg`}
|
||||
/>
|
||||
<p className="text-[11px]">{tooltip.region.country}</p>
|
||||
<p className="text-[10px]">{tooltip.region.country}</p>
|
||||
</div>
|
||||
<p
|
||||
className={`text-[11px] ${
|
||||
className={`text-[10px] ${
|
||||
tooltip.region.name === undefined ? 'text-foreground-light' : ''
|
||||
}`}
|
||||
>
|
||||
@@ -250,7 +251,7 @@ const MapView = ({
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-10 rounded-sm"
|
||||
src={`${BASE_PATH}/img/regions/${selectedRegion.key}.svg`}
|
||||
src={`${BASE_PATH}/img/regions/${selectedRegion.region}.svg`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<HoverCard onOpenChange={() => setOpenState(!open)} openDelay={280}>
|
||||
<HoverCardTrigger asChild className="group" onClick={(e) => e.stopPropagation()}>
|
||||
<Link href={`/project/${project?.ref}/settings/compute-and-disk`}>
|
||||
<div>
|
||||
<ComputeBadge infraComputeSize={project.infra_compute_size} />
|
||||
</Link>
|
||||
</div>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent
|
||||
side="bottom"
|
||||
|
||||
@@ -20,4 +20,6 @@ export const organizationKeys = {
|
||||
['organizations', slug, 'validate-token', token] as const,
|
||||
projectClaim: (slug: string, token: string) =>
|
||||
['organizations', slug, 'project-claim', token] as const,
|
||||
availableRegions: (slug: string | undefined, cloudProvider: string) =>
|
||||
['organizations', slug, 'available-regions', cloudProvider] as const,
|
||||
}
|
||||
@@ -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<typeof getOrganizationAvailableRegions>
|
||||
>
|
||||
export type OrganizationAvailableRegionsError = ResponseError
|
||||
|
||||
export const useOrganizationAvailableRegionsQuery = <TData = OrganizationAvailableRegionsData>(
|
||||
{ slug, cloudProvider }: OrganizationAvailableRegionsVariables,
|
||||
{
|
||||
enabled = true,
|
||||
...options
|
||||
}: UseQueryOptions<
|
||||
OrganizationAvailableRegionsData,
|
||||
OrganizationAvailableRegionsError,
|
||||
TData
|
||||
> = {}
|
||||
) =>
|
||||
useQuery<OrganizationAvailableRegionsData, OrganizationAvailableRegionsError, TData>(
|
||||
organizationKeys.availableRegions(slug, cloudProvider),
|
||||
({ signal }) => getOrganizationAvailableRegions({ slug, cloudProvider }, signal),
|
||||
{ enabled: enabled && typeof slug !== 'undefined', ...options }
|
||||
)
|
||||
@@ -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 && {
|
||||
|
||||
@@ -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={() => (
|
||||
<img
|
||||
alt="region icon"
|
||||
className="w-5 rounded-sm"
|
||||
src={`${router.basePath}/img/regions/${Object.keys(AWS_REGIONS)[i]}.svg`}
|
||||
src={`${BASE_PATH}/img/regions/${Object.values(AWS_REGIONS)[i].code}.svg`}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -166,13 +166,12 @@ const Home: NextPageWithLayout = () => {
|
||||
>
|
||||
Tables
|
||||
</Link>
|
||||
<p className="text-2xl tabular-nums">
|
||||
{isLoadingTables ? (
|
||||
<ShimmeringLoader className="w-full h-[32px] w-6 p-0" />
|
||||
) : (
|
||||
tablesCount
|
||||
)}
|
||||
</p>
|
||||
|
||||
{isLoadingTables ? (
|
||||
<ShimmeringLoader className="w-full h-[32px] w-6 p-0" />
|
||||
) : (
|
||||
<p className="text-2xl tabular-nums">{tablesCount}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{IS_PLATFORM && (
|
||||
@@ -183,13 +182,11 @@ const Home: NextPageWithLayout = () => {
|
||||
>
|
||||
Functions
|
||||
</Link>
|
||||
<p className="text-2xl tabular-nums">
|
||||
{isLoadingFunctions ? (
|
||||
<ShimmeringLoader className="w-full h-[32px] w-6 p-0" />
|
||||
) : (
|
||||
functionsCount
|
||||
)}
|
||||
</p>
|
||||
{isLoadingFunctions ? (
|
||||
<ShimmeringLoader className="w-full h-[32px] w-6 p-0" />
|
||||
) : (
|
||||
<p className="text-2xl tabular-nums">{functionsCount}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -201,13 +198,11 @@ const Home: NextPageWithLayout = () => {
|
||||
>
|
||||
Replicas
|
||||
</Link>
|
||||
<p className="text-2xl tabular-nums">
|
||||
{isLoadingReplicas ? (
|
||||
<ShimmeringLoader className="w-full h-[32px] w-6 p-0" />
|
||||
) : (
|
||||
replicasCount
|
||||
)}
|
||||
</p>
|
||||
{isLoadingReplicas ? (
|
||||
<ShimmeringLoader className="w-full h-[32px] w-6 p-0" />
|
||||
) : (
|
||||
<p className="text-2xl tabular-nums">{replicasCount}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-za" viewBox="0 0 640 480">
|
||||
<defs>
|
||||
<clipPath id="za-a">
|
||||
<path fill-opacity=".7" d="M-71.9 0h682.7v512H-71.9z"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#za-a)" transform="translate(67.4) scale(.93748)">
|
||||
<g fill-rule="evenodd" stroke-width="1pt">
|
||||
<path d="M-71.9 407.8V104.4L154 256.1-72 407.8z"/>
|
||||
<path fill="#00c" d="m82.2 512.1 253.6-170.6H696V512H82.2z"/>
|
||||
<path fill="red" d="M66 0h630v170.8H335.7S69.3-1.7 66 0z"/>
|
||||
<path fill="#fc0" d="M-71.9 64v40.4L154 256-72 407.8v40.3l284.5-192L-72 64z"/>
|
||||
<path fill="#093" d="M-71.9 64V0h95l301.2 204h371.8v104.2H324.3L23 512h-94.9v-63.9l284.4-192L-71.8 64z"/>
|
||||
<path fill="#fff" d="M23 0h59.2l253.6 170.7H696V204H324.3L23 .1zm0 512.1h59.2l253.6-170.6H696v-33.2H324.3L23 512z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 856 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480" fill="none"><path fill="#313131" d="M0 0h640v480H0z"/><g clip-path="url(#a)"><path fill="#2E8BC0" d="M320 400c88.366 0 160-71.634 160-160S408.366 80 320 80s-160 71.634-160 160 71.634 160 160 160Z"/><path fill="#3ECF8E" d="M427.822 291.093c-9.04-3.466-7.431-2.204-13.004-14.053-5.574-11.84-8.951-15.653-21.485-17.04-12.542-1.378-12.071-.4-15.555-6.667-3.485-6.275-17.618 1.698-26.667 5.191-9.075 3.485-6.835 11.076-9.627 10.365a42.945 42.945 0 0 1-16.826-8.062c-6.969-5.583 5.182-9.938 8.675-13.414 3.494-3.493-6.577-1.591-10.071-3.697-3.493-2.08-9.742 4.186-15.315-1.405-5.583-5.573-2.089-13.938-2.783-20.907-.702-6.977 9.049-8.364 15.325-9.76 6.267-1.386 11.138.009 11.138 8.365 0 8.364 7.671 11.155 5.582 1.387-2.089-9.769 2.089-12.543 4.889-17.423 2.782-4.88 7.662-11.857 13.929-22.311 6.275-10.453 10.462-5.582 18.124-4.835 7.671.755 9.76-.738 15.333-.036 5.556.702 0-6.969 2.098-14.631 2.08-7.662 9.751-6.276 17.431-4.178 7.663 2.089-7.68-9.769-10.462-16.035-2.8-6.276-10.462 0-15.324-10.454-4.916-10.453-11.769-17.217-18.045-18.151-6.275-.942 2 13.28-2.88 20.942-4.898 7.663-12.4 6.463-20.089 6.463-7.653 0-14.764-6.463-9.182-16.223 5.565-9.751 16.205-8.746 21.778-17.111 5.582-8.355-19.822-7.59-28.889-10.373-9.067-2.791-19.947-2.764-29.004 4.204-9.067 6.96-28.454 9.138-36.125 11.92-7.662 2.783-9.04-4.622-10.427-10.204-.622-2.48-3.448-3.262-7.431-3.173a160.711 160.711 0 0 0-50.364 43.742c2.107 7.173 12.311-1.68 14.311-3.68 2.089-2.089 12.542.702 13.929 8.364 1.395 7.663.702 16.045-2.08 23.014-2.791 6.96 6.969 16.72 12.542 26.48 5.582 9.769 11.849 9.76 14.64 9.76 2.782 0 18.818 6.275 18.116 13.937-.694 7.672 14.64 14.632 20.915 19.512 6.276 4.897 10.24 9.706 18.382 11.111 8.152 1.404 17.68 14.729 21.858 16.133 3.209 1.067 8.64 6.196 11.92 7.787-4.311 5.457-12.658 16.666-12.658 21.831 0 6.986 9.112 17.964 12.596 20.746 3.476 2.774 13.484 4.889 15.556 11.849 2.097 6.969-1.458 30.427-7.716 36.694-6.293 6.266-8.578 21.084-8.578 21.084s2.214 4.809 9.627 0c8.195-5.307 21.92-20.418 27.404-23.707a184.585 184.585 0 0 0 30.374-22.96c9.76-9.057 6.222-15.404 11.111-24.444 4.88-9.058 11.191-8.649 19.546-16.293 8.374-7.68 2.525-6.16-6.542-9.654Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M160 80h320v320H160z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480" fill="none"><path fill="#313131" d="M0 0h640v480H0z"/><g clip-path="url(#a)"><path fill="#2E8BC0" d="M320 400c88.366 0 160-71.634 160-160S408.366 80 320 80s-160 71.634-160 160 71.634 160 160 160Z"/><path fill="#3ECF8E" d="M184.996 305.556c-.552-8.889-.552-10.552-.552-21.112 0-10.56 4.996-8.888 10-13.884 5.005-4.996 3.894-5.556 12.223-11.031 8.329-5.476 3.893-11.742 3.333-15.076-.56-3.333-5.556-.56-13.893.543-8.338 1.102-5.547-2.774-10.551-4.996-5.005-2.222-7.218-5.556-10.56-15.556-3.343-10-3.894-12.782-2.774-21.111s5.005-.56 5.556 8.329c.551 8.889 8.338 5.556 8.338 11.111 0 5.556 11.111 11.663 13.884 13.334 2.773 1.671 10.56-8.338 13.333-11.111 2.774-2.774 6.116-6.667 7.218-8.889 1.102-2.223 15.005-3.894 17.778-3.894 2.773 0 9.44 8.338 9.44 12.223 0 3.884 3.333 14.444 6.115 20.551 2.783 6.106 8.889 7.217 14.445 11.662 5.555 4.444 8.338.551 8.338.551s-2.223-9.44-2.223-17.227c0-7.786 6.667-14.444 6.667-14.444s10.56 7.778 11.111 10c.551 2.222 8.889 10 9.44 13.884.551 3.885 4.996 8.889 13.182 10 8.187 1.111 2.374-9.44 5.147-16.666 2.773-7.227 6.667-8.338 11.662-9.44 4.996-1.103 8.889-5.556 12.223-10 3.333-4.445 9.44-10.56 8.888-15.556-.551-4.995-2.222-8.338-4.444-14.444-2.222-6.107 6.667-8.338 10.56-15.556 3.893-7.218 0 0 8.889-2.222s4.995-2.222 6.667-5.556c1.671-3.333 2.773-6.666 1.111-12.782-1.663-6.115-7.778 0-13.885 0-6.106 0 2.231-8.302 2.782-14.969.552-6.666 8.338-4.995 13.885-5.555 5.547-.56 7.218 7.217 8.889 8.889 1.671 1.671 18.889-11.112 23.333-17.227 4.445-6.116-3.884-4.436-1.662-7.013 2.222-2.578-13.333-3.103-19.449-4.089-6.116-.987-21.662-1.671-27.769-5.44-6.107-3.769-29.44-.925-35.555 2.106-6.116 3.031-10-.257-12.783-4.444-2.782-4.187-14.444-2.089-17.777-4.445-3.334-2.355-6.667 3.885-11.112 5.556-4.444 1.671-6.115 2.222-11.662-1.111-5.546-3.333 1.662-7.227-6.115-10-5.209-1.858-11.449-.773-21.156-.987C194.684 125.262 160 178.551 160 240c0 30.4 8.631 58.711 23.351 82.907 2.738-4.605 2.053-10.818 1.645-17.351Zm128.888-10.552c3.894.552 15.005 0 6.116-7.217-8.889-7.218-13.884-1.671-12.782-10 1.102-8.329-5.556-8.338-5.556-8.338 0 6.115-4.444 12.782 0 18.889 4.445 6.106 8.338 6.106 12.222 6.666Z"/><path fill="#3ECF8E" d="M370.56 202.222c-8.889-7.218-2.222-4.995-1.111-13.333 1.111-8.338-5.556-8.338-5.556-8.338 0 6.116-4.444 12.782 0 18.889 4.445 6.107-8.889 11.111-4.995 11.662 3.893.551 20.551-1.662 11.662-8.88Zm-34.489 86.667c5.511 6.115 3.378 0 10.596-2.773 7.217-2.774-6.116-8.889-10.56-12.223-4.445-3.333-8.863-3.457-12.747 3.894-4.409 8.329 7.2 4.986 12.711 11.102ZM401.111 300c-2.773-3.333-8.889-4.996-15.555-4.844-6.667.16-6.116-7.378-12.783-6.827-6.666.551-11.431-4.48-14.444-6.036-6.551-3.377-2.222 4.365 0 12.854 2.222 8.489 10.56 2.062 18.329 6.506 7.769 4.445 8.338-1.671 15.555.551 7.218 2.223 10 7.218 16.925 6.667 6.924-.551-5.245-5.538-8.027-8.871Zm-14.444 24.444c-2.774-5.555-10.898-10.56-14.231-13.377-3.334-2.818-8.552-3.769-12.996-2.134-4.444 1.636-7.218 0-9.44-4.4-2.222-4.4-6.116-2.862-9.44-2.311-3.324.551-16.667 6.116-24.444 10-7.778 3.885-11.316 7.262-16.418 12.222-7.982 7.769-3.582 2.774 0 7.778 3.582 4.996-3.929 20.551-4.48 29.44-.551 8.889 14.24-2.773 19.795-3.893 5.556-1.12 3.92-4.445 8.365-7.778 4.444-3.333 7.333-4.115 12.213.329 4.88 4.444 11.271 8.56 11.271 13.556 0 4.995 17.591 13.333 24.258 10 6.667-3.334 10.56-10 16.667-15.556 6.106-5.556 3.893-16.107 5.555-22.773 1.662-6.667-3.902-5.547-6.675-11.103Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M160 80h320v320H160z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
File renamed without changes.
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="640" height="480" fill="none"><path fill="#313131" d="M0 0h640v480H0z"/><g clip-path="url(#a)"><path fill="#2E8BC0" d="M320 400c88.366 0 160-71.634 160-160S408.366 80 320 80s-160 71.634-160 160 71.634 160 160 160Z"/><path fill="#3ECF8E" d="M388.587 95.609c-9.085 5.049-16.64 13.582-26.916 10.498-16.667-4.996-38.889-16.107-53.333-2.223-14.445 13.885-17.778 26.667 0 26.116 17.778-.551 30-21.671 38.889-12.782 8.889 8.889 6.657 16.115-14.445 18.889-21.102 2.773-43.893 6.115-56.115 6.115-12.223 0-15.005 7.218-6.667 15.005 8.338 7.786-5.004 8.329-18.889 16.106-13.884 7.778 3.333 11.111 15.005 17.778 11.671 6.667 20.551-1.671 25.555-12.782 5.005-11.111 26.498-24.445 35.467-22.773 8.978 1.671 8.978 6.115 7.306 13.884-1.671 7.769 6.667 5.556 7.218-3.333.551-8.889 10.56-15.556 18.329-16.107 7.769-.551 14.445 10.56 5.556 16.107-8.889 5.546-17.778 10-6.667 12.782 11.111 2.782 18.889 17.227 6.116 23.333-12.774 6.107-34.996 10-44.996 4.996-10-5.005-32.782-12.222-38.889-8.338-6.107 3.884-9.44 7.911-16.667 10.622-7.217 2.711-36.666 16.045-37.226 33.271-.56 17.227-1.102 36.676 10.56 37.227 11.662.551 40-7.218 48.889-14.444 8.889-7.227 21.111-5.556 24.995 2.773 3.885 8.329 1.111 13.333-2.773 26.667-3.885 13.333 2.542 20 8.773 31.662 6.231 11.662 11.227 18.329 11.227 26.666 0 8.338 8.889 16.667 22.222 2.774 13.333-13.894 25.556-41.111 31.111-51.111 5.556-10 10-32.223 16.667-36.667 6.667-4.444 17.227-15.004 9.44-13.333-7.787 1.671-23.333-.551-27.218-11.663-3.884-11.111-20.551-32.222-12.782-34.444 7.769-2.222 16.667 12.356 20 19.236 3.333 6.88 7.778 15.208 14.444 17.431 6.667 2.222 21.111-14.871 25.556-17.431 4.444-2.569 1.111-13.12-7.778-12.009-8.889 1.111-20.551 0-20.551-5.547s11.111-12.782 20-11.111c8.889 1.671 15.556 4.444 21.111 11.111 5.556 6.667 16.667 18.889 21.111 26.667 4.445 7.777 7.778 8.889 10-4.996 1.476-9.227 3.44-14.302 5.245-19.751-9.005-51.813-42.845-94.96-88.88-116.871Z"/></g><defs><clipPath id="a"><path fill="#fff" d="M160 80h320v320H160z"/></clipPath></defs></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user