diff --git a/studio/components/to-be-cleaned/ProductEmptyState.tsx b/studio/components/to-be-cleaned/ProductEmptyState.tsx index 09eaff00b57..ab85f4820cb 100644 --- a/studio/components/to-be-cleaned/ProductEmptyState.tsx +++ b/studio/components/to-be-cleaned/ProductEmptyState.tsx @@ -9,6 +9,7 @@ interface ProductEmptyStateProps { infoButtonLabel?: string infoButtonUrl?: string onClickCta?: () => void + loading?: boolean disabled?: boolean disabledMessage?: string } @@ -21,6 +22,7 @@ const ProductEmptyState = ({ infoButtonLabel = '', infoButtonUrl = '', onClickCta = () => {}, + loading = false, disabled = false, disabledMessage = '', }: PropsWithChildren) => { @@ -39,7 +41,12 @@ const ProductEmptyState = ({ {ctaButtonLabel && onClickCta && ( - diff --git a/studio/data/database/hooks-enable-mutation.ts b/studio/data/database/hooks-enable-mutation.ts new file mode 100644 index 00000000000..2d350a3be10 --- /dev/null +++ b/studio/data/database/hooks-enable-mutation.ts @@ -0,0 +1,45 @@ +import { useMutation, UseMutationOptions } from '@tanstack/react-query' +import { toast } from 'react-hot-toast' + +import { post } from 'data/fetchers' +import { ResponseError } from 'types' + +export type HooksEnableVariables = { + ref: string +} + +export async function enableDatabaseWebhooks({ ref }: HooksEnableVariables) { + const { data, error } = await post('/platform/database/{ref}/hook-enable', { + params: { path: { ref } }, + }) + if (error) throw error + return data +} + +type HooksEnableData = Awaited> + +export const useHooksEnableMutation = ({ + onSuccess, + onError, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + return useMutation( + (vars) => enableDatabaseWebhooks(vars), + { + async onSuccess(data, variables, context) { + await onSuccess?.(data, variables, context) + }, + async onError(data, variables, context) { + if (onError === undefined) { + toast.error(`Failed to enable webhooks: ${data.message}`) + } else { + onError(data, variables, context) + } + }, + ...options, + } + ) +} diff --git a/studio/data/organizations/organization-create-mutation.ts b/studio/data/organizations/organization-create-mutation.ts new file mode 100644 index 00000000000..d3aa1e97de7 --- /dev/null +++ b/studio/data/organizations/organization-create-mutation.ts @@ -0,0 +1,53 @@ +import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' +import { toast } from 'react-hot-toast' + +import { post } from 'data/fetchers' +import { ResponseError } from 'types' +import { organizationKeys } from './keys' + +export type OrganizationCreateVariables = { + name: string + kind?: string + size?: string +} + +export async function createOrganization({ name, kind, size }: OrganizationCreateVariables) { + const { data, error } = await post('/platform/organizations', { + // @ts-ignore [Joshen] Generated API spec is wrong? + body: { name, kind, ...(kind == 'COMPANY' ? { size } : {}) }, + }) + + if (error) throw error + return data +} + +type OrganizationCreateData = Awaited> + +export const useOrganizationCreateMutation = ({ + onSuccess, + onError, + ...options +}: Omit< + UseMutationOptions, + 'mutationFn' +> = {}) => { + const queryClient = useQueryClient() + + return useMutation( + (vars) => createOrganization(vars), + { + async onSuccess(data, variables, context) { + await queryClient.invalidateQueries(organizationKeys.list()) + await onSuccess?.(data, variables, context) + }, + async onError(data, variables, context) { + if (onError === undefined) { + toast.error(`Failed to create organization: ${data.message}`) + } else { + onError(data, variables, context) + } + }, + ...options, + } + ) +} diff --git a/studio/data/projects/project-create-mutation.ts b/studio/data/projects/project-create-mutation.ts index 09ab5b925cd..6fa51da915c 100644 --- a/studio/data/projects/project-create-mutation.ts +++ b/studio/data/projects/project-create-mutation.ts @@ -12,7 +12,11 @@ export type ProjectCreateVariables = { dbPass: string dbRegion: string dbSql?: string + dbPricingTierId?: string + cloudProvider?: string configurationId?: string + authSiteUrl?: string + customSupabaseRequest?: object } export async function createProject({ @@ -21,18 +25,25 @@ export async function createProject({ dbPass, dbRegion, dbSql, + dbPricingTierId = PRICING_TIER_PRODUCT_IDS.FREE, + cloudProvider = PROVIDERS.AWS.id, configurationId, + authSiteUrl, + customSupabaseRequest, }: ProjectCreateVariables) { const response = await post(`${API_URL}/projects`, { - cloud_provider: PROVIDERS.AWS.id, // hardcoded for DB instances to be under AWS + cloud_provider: cloudProvider, org_id: organizationId, name, db_pass: dbPass, db_region: dbRegion, db_sql: dbSql, - db_pricing_tier_id: PRICING_TIER_PRODUCT_IDS.FREE, - // auth_site_url: _store.selectedVercelProjectUrl, + db_pricing_tier_id: dbPricingTierId, + auth_site_url: authSiteUrl, vercel_configuration_id: configurationId, + ...(customSupabaseRequest !== undefined && { + custom_supabase_internal_requests: customSupabaseRequest, + }), }) if (response.error) throw response.error return response as ProjectBase @@ -59,7 +70,7 @@ export const useProjectCreateMutation = ({ }, async onError(data, variables, context) { if (onError === undefined) { - toast.error(`Failed to create project: ${data.message}`) + toast.error(`Failed to create new project: ${data.message}`) } else { onError(data, variables, context) } diff --git a/studio/lib/common/fetch/base.ts b/studio/lib/common/fetch/base.ts index 42c4d1fc8be..c7140a3820d 100644 --- a/studio/lib/common/fetch/base.ts +++ b/studio/lib/common/fetch/base.ts @@ -1,7 +1,6 @@ import { getAccessToken } from 'lib/gotrue' import { isUndefined } from 'lodash' import { SupaResponse } from 'types/base' -import { Session } from '@supabase/gotrue-js' export function handleError(e: any, requestId: string): SupaResponse { const message = e?.message ? `An error has occurred: ${e.message}` : 'An error has occurred' diff --git a/studio/pages/new/[slug].tsx b/studio/pages/new/[slug].tsx index 4842dfbac91..3dbced890a0 100644 --- a/studio/pages/new/[slug].tsx +++ b/studio/pages/new/[slug].tsx @@ -4,7 +4,7 @@ import generator from 'generate-password' import { debounce, isUndefined } from 'lodash' import { observer } from 'mobx-react-lite' import Link from 'next/link' -import Router, { useRouter } from 'next/router' +import { useRouter } from 'next/router' import { useEffect, useRef, useState } from 'react' import { Alert, @@ -32,12 +32,14 @@ import PasswordStrengthBar from 'components/ui/PasswordStrengthBar' import { useFreeProjectLimitCheckQuery } from 'data/organizations/free-project-limit-check-query' import { useOrganizationPaymentMethodsQuery } from 'data/organizations/organization-payment-methods-query' import { useOrganizationsQuery } from 'data/organizations/organizations-query' +import { + ProjectCreateVariables, + useProjectCreateMutation, +} from 'data/projects/project-create-mutation' import { useProjectsQuery } from 'data/projects/projects-query' import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query' import { useCheckPermissions, useFlag, useStore, withAuth } from 'hooks' -import { post } from 'lib/common/fetch' import { - API_URL, AWS_REGIONS, CloudProvider, DEFAULT_MINIMUM_PASSWORD_STRENGTH, @@ -70,7 +72,6 @@ const Wizard: NextPageWithLayout = () => { const [dbPass, setDbPass] = useState('') const [dbRegion, setDbRegion] = useState(PROVIDERS[cloudProvider].default_region) const [dbPricingTierKey, setDbPricingTierKey] = useState(PRICING_TIER_DEFAULT_KEY) - const [newProjectedLoading, setNewProjectLoading] = useState(false) const [passwordStrengthMessage, setPasswordStrengthMessage] = useState('') const [passwordStrengthWarning, setPasswordStrengthWarning] = useState('') const [passwordStrengthScore, setPasswordStrengthScore] = useState(0) @@ -93,6 +94,12 @@ const Wizard: NextPageWithLayout = () => { enabled: currentOrg?.subscription_id != undefined, }) + const { mutate: createProject, isLoading: isCreatingNewProject } = useProjectCreateMutation({ + onSuccess: (res) => { + router.push(`/project/${res.ref}/building`) + }, + }) + const orgProjectCount = (allProjects || []).filter( (proj) => proj.organization_id === currentOrg?.id ).length @@ -186,43 +193,31 @@ const Wizard: NextPageWithLayout = () => { } const onClickNext = async () => { - setNewProjectLoading(true) + if (!currentOrg) return console.error('Unable to retrieve current organization') const dbTier = dbPricingTierKey === 'PRO' && !isSpendCapEnabled ? 'PAYG' : dbPricingTierKey - - const data: Record = { - cloud_provider: cloudProvider, - org_id: currentOrg?.id, + const data: ProjectCreateVariables = { + cloudProvider, + organizationId: currentOrg.id, name: projectName.trim(), - db_pass: dbPass, - db_region: dbRegion, - db_pricing_tier_id: (PRICING_TIER_PRODUCT_IDS as any)[dbTier], + dbPass: dbPass, + dbRegion: dbRegion, + dbPricingTierId: (PRICING_TIER_PRODUCT_IDS as any)[dbTier], } if (postgresVersion) { if (!postgresVersion.match(/1[2-9]\..*/)) { - setNewProjectLoading(false) - ui.setNotification({ + return ui.setNotification({ category: 'error', message: `Invalid Postgres version, should start with a number between 12-19, a dot and additional characters, i.e. 15.2 or 15.2.0-3`, }) - return } - data['custom_supabase_internal_requests'] = { + data['customSupabaseRequest'] = { ami: { search_tags: { 'tag:postgresVersion': postgresVersion } }, } } - const response = await post(`${API_URL}/projects`, data) - if (response.error) { - setNewProjectLoading(false) - ui.setNotification({ - error: response.error, - category: 'error', - message: `Failed to create new project: ${response.error.message}`, - }) - } else { - router.push(`/project/${response.ref}/building`) - } + + createProject(data) } // [Joshen] Refactor: DB Password could be a common component @@ -263,7 +258,11 @@ const Wizard: NextPageWithLayout = () => { } footer={
-
@@ -272,8 +271,8 @@ const Wizard: NextPageWithLayout = () => { )} diff --git a/studio/pages/new/index.tsx b/studio/pages/new/index.tsx index f89c1d4883f..be52ba18366 100644 --- a/studio/pages/new/index.tsx +++ b/studio/pages/new/index.tsx @@ -1,4 +1,3 @@ -import { useQueryClient } from '@tanstack/react-query' import { observer } from 'mobx-react-lite' import { useRouter } from 'next/router' import { useState } from 'react' @@ -6,10 +5,8 @@ import { Button, Input, Listbox } from 'ui' import { WizardLayout } from 'components/layouts' import Panel from 'components/ui/Panel' -import { invalidateOrganizationsQuery } from 'data/organizations/organizations-query' +import { useOrganizationCreateMutation } from 'data/organizations/organization-create-mutation' import { useStore } from 'hooks' -import { post } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' import { NextPageWithLayout } from 'types' const ORG_KIND_TYPES = { @@ -35,14 +32,19 @@ const ORG_SIZE_DEFAULT = '1' * No org selected yet, create a new one */ const Wizard: NextPageWithLayout = () => { - const queryClient = useQueryClient() const { ui } = useStore() const router = useRouter() const [orgName, setOrgName] = useState('') const [orgKind, setOrgKind] = useState(ORG_KIND_DEFAULT) const [orgSize, setOrgSize] = useState(ORG_SIZE_DEFAULT) - const [newOrgLoading, setNewOrgLoading] = useState(false) + + const { mutate: createOrganization, isLoading: newOrgLoading } = useOrganizationCreateMutation({ + onSuccess: async (org: any) => { + // [Joshen] API spec is wrong? its returning org type as only having id and name + router.push(`/new/${org.slug}`) + }, + }) function validateOrgName(name: any) { return name.length >= 1 @@ -65,29 +67,14 @@ const Wizard: NextPageWithLayout = () => { const trimmedOrgName = orgName ? orgName.trim() : '' const isOrgNameValid = validateOrgName(trimmedOrgName) if (!isOrgNameValid) { - ui.setNotification({ category: 'error', message: 'Organization name is empty' }) - return + return ui.setNotification({ category: 'error', message: 'Organization name is empty' }) } - setNewOrgLoading(true) - const response = await post(`${API_URL}/organizations`, { + createOrganization({ name: trimmedOrgName, kind: orgKind, ...(orgKind == 'COMPANY' ? { size: orgSize } : {}), }) - - if (response.error) { - setNewOrgLoading(false) - ui.setNotification({ - error: response.error, - category: 'error', - message: `Failed to create organization: ${response.error?.message ?? response.error}`, - }) - } else { - const org = response - await invalidateOrganizationsQuery(queryClient) - router.push(`/new/${org.slug}`) - } } return ( diff --git a/studio/pages/project/[ref]/database/hooks/index.tsx b/studio/pages/project/[ref]/database/hooks/index.tsx index 12e370a7d77..86ccc236c4b 100644 --- a/studio/pages/project/[ref]/database/hooks/index.tsx +++ b/studio/pages/project/[ref]/database/hooks/index.tsx @@ -1,25 +1,22 @@ import { PermissionAction } from '@supabase/shared-types/out/constants' import clsx from 'clsx' +import { useParams } from 'common' import { observer } from 'mobx-react-lite' import { useEffect, useState } from 'react' +import { IconLoader } from 'ui' -import { useParams } from 'common' import DeleteHookModal from 'components/interfaces/Database/Hooks/DeleteHookModal' import EditHookPanel from 'components/interfaces/Database/Hooks/EditHookPanel' import HooksList from 'components/interfaces/Database/Hooks/HooksList/HooksList' import { DatabaseLayout } from 'components/layouts' -import { useProjectContext } from 'components/layouts/ProjectLayout/ProjectContext' import ProductEmptyState from 'components/to-be-cleaned/ProductEmptyState' import NoPermission from 'components/ui/NoPermission' +import { useHooksEnableMutation } from 'data/database/hooks-enable-mutation' import { useCheckPermissions, useStore } from 'hooks' -import { post } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' import { NextPageWithLayout } from 'types' -import { IconLoader } from 'ui' const HooksPage: NextPageWithLayout = () => { const { meta, ui } = useStore() - const { project } = useProjectContext() const { ref } = useParams() const schemas = meta.schemas.list() @@ -33,24 +30,23 @@ const HooksPage: NextPageWithLayout = () => { const canReadWebhooks = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_READ, 'triggers') const canCreateWebhooks = useCheckPermissions(PermissionAction.TENANT_SQL_ADMIN_WRITE, 'triggers') - useEffect(() => { - if (ui.selectedProjectRef) meta.hooks.load() - }, [ui.selectedProjectRef]) - - const enableHooksForProject = async () => { - const res = await post(`${API_URL}/database/${ref}/hook-enable`, {}) - if (res.error) { - ui.setNotification({ - category: 'error', - message: `Failed to enable webhooks: ${res.error.message}`, - }) - } else { + const { mutate: enableHooks, isLoading: isEnablingHooks } = useHooksEnableMutation({ + onSuccess: () => { meta.schemas.load() ui.setNotification({ category: 'success', message: `Successfully enabled webhooks`, }) - } + }, + }) + + useEffect(() => { + if (ui.selectedProjectRef) meta.hooks.load() + }, [ui.selectedProjectRef]) + + const enableHooksForProject = async () => { + if (!ref) return console.error('Project ref is required') + enableHooks({ ref }) } const createHook = () => { @@ -89,8 +85,11 @@ const HooksPage: NextPageWithLayout = () => { title="Database Webhooks" ctaButtonLabel="Enable webhooks" onClickCta={() => enableHooksForProject()} - disabled={!canCreateWebhooks} - disabledMessage="You need additional permissions to enable webhooks" + loading={isEnablingHooks} + disabled={isEnablingHooks || !canCreateWebhooks} + disabledMessage={ + !canCreateWebhooks ? 'You need additional permissions to enable webhooks' : undefined + } >

Database Webhooks can be used to trigger serverless functions or send requests to an diff --git a/studio/pages/vercel/setupOrg.tsx b/studio/pages/vercel/setupOrg.tsx index fa47213c7ab..e2911b40e59 100644 --- a/studio/pages/vercel/setupOrg.tsx +++ b/studio/pages/vercel/setupOrg.tsx @@ -2,13 +2,11 @@ import { runInAction } from 'mobx' import { observer, useLocalObservable } from 'mobx-react-lite' import { useRouter } from 'next/router' import { ChangeEvent, createContext, useContext, useEffect, useState } from 'react' +import { Button, Input, Select } from 'ui' import VercelIntegrationLayout from 'components/layouts/VercelIntegrationLayout' +import { useOrganizationCreateMutation } from 'data/organizations/organization-create-mutation' import { useOrganizationsQuery } from 'data/organizations/organizations-query' -import { useStore } from 'hooks' -import { post } from 'lib/common/fetch' -import { API_URL } from 'lib/constants' -import { Button, Input, Select } from 'ui' const PageContext = createContext(null) function SetupOrg() { @@ -74,38 +72,19 @@ function SetupOrg() { export default observer(SetupOrg) const CreateOrganization = observer(({}) => { - const PageState: any = useContext(PageContext) const router = useRouter() - - const { ui, app } = useStore() + const PageState: any = useContext(PageContext) const { data: organizations } = useOrganizationsQuery() const [orgName, setOrgName] = useState('') - const [loading, setLoading] = useState(false) - async function onClick() { - setLoading(true) - const response = await post(`${API_URL}/organizations`, { - name: orgName, - }) - if (response.error) { - ui.setNotification({ - category: 'error', - message: `Failed to set up organization: ${response.error.message}`, - }) - setLoading(false) - } else { - const org = response + const { mutate: createOrganization, isLoading: isCreating } = useOrganizationCreateMutation({ + onSuccess: (org) => { PageState.selectedOrg = org - const query = new URLSearchParams(PageState.queryParams).toString() router.push(`/vercel/setupProject?${query}`) - } - } - - function onOrgNameChange(e: any) { - setOrgName(e.target.value) - } + }, + }) return (

@@ -126,10 +105,14 @@ const CreateOrganization = observer(({}) => { placeholder="Organization name" descriptionText="What's the name of your company or team?" value={orgName} - onChange={onOrgNameChange} + onChange={(e: any) => setOrgName(e.target.value)} />
-
diff --git a/studio/pages/vercel/setupProject.tsx b/studio/pages/vercel/setupProject.tsx index 6ed3968626f..8b950d33569 100644 --- a/studio/pages/vercel/setupProject.tsx +++ b/studio/pages/vercel/setupProject.tsx @@ -1,31 +1,30 @@ -import { toast } from 'react-hot-toast' -import { createContext, useEffect, useContext, useRef, useState, ChangeEvent } from 'react' -import { useRouter } from 'next/router' -import { observer, useLocalObservable } from 'mobx-react-lite' -import { makeAutoObservable } from 'mobx' -import { debounce } from 'lodash' -import { Button, Input, Listbox } from 'ui' -import { Dictionary } from 'components/grid' import generator from 'generate-password' -import { useStore } from 'hooks' -import { post } from 'lib/common/fetch' -import { passwordStrength } from 'lib/helpers' -import { - PROVIDERS, - DEFAULT_MINIMUM_PASSWORD_STRENGTH, - API_URL, - PRICING_TIER_PRODUCT_IDS, - AWS_REGIONS, -} from 'lib/constants' -import { VERCEL_INTEGRATION_CONFIGS } from 'lib/vercelConfigs' +import { debounce } from 'lodash' +import { makeAutoObservable } from 'mobx' +import { observer, useLocalObservable } from 'mobx-react-lite' +import { useRouter } from 'next/router' +import { ChangeEvent, createContext, useContext, useEffect, useRef, useState } from 'react' +import { toast } from 'react-hot-toast' +import { Button, Input, Listbox } from 'ui' + +import { Dictionary } from 'components/grid' +import VercelIntegrationLayout from 'components/layouts/VercelIntegrationLayout' import { createVercelEnv, fetchVercelProject, prepareVercelEvns, } from 'components/to-be-cleaned/Integration/Vercel.utils' -import VercelIntegrationLayout from 'components/layouts/VercelIntegrationLayout' import Loading from 'components/ui/Loading' import PasswordStrengthBar from 'components/ui/PasswordStrengthBar' +import { useProjectCreateMutation } from 'data/projects/project-create-mutation' +import { + AWS_REGIONS, + DEFAULT_MINIMUM_PASSWORD_STRENGTH, + PRICING_TIER_PRODUCT_IDS, + PROVIDERS, +} from 'lib/constants' +import { passwordStrength } from 'lib/helpers' +import { VERCEL_INTEGRATION_CONFIGS } from 'lib/vercelConfigs' interface ISetupProjectStore { token: string @@ -144,19 +143,49 @@ const Connecting = () => ( ) const CreateProject = observer(() => { - const _store = useContext(PageContext) const router = useRouter() - const { ui } = useStore() + const _store = useContext(PageContext) + const [projectName, setProjectName] = useState('') const [dbPass, setDbPass] = useState('') const [passwordStrengthMessage, setPasswordStrengthMessage] = useState('') const [passwordStrengthScore, setPasswordStrengthScore] = useState(-1) const [dbRegion, setDbRegion] = useState(PROVIDERS.AWS.default_region) - const [loading, setLoading] = useState(false) + const delayedCheckPasswordStrength = useRef( debounce((value: string) => checkPasswordStrength(value), 300) ).current + const { mutate: createProject, isLoading } = useProjectCreateMutation({ + onSuccess: async (res) => { + const project = { ...res, db_host: `db.${res.ref}.supabase.co`, db_password: dbPass } + _store.supabaseProjectRef = project.ref + + const requiredEnvs = + VERCEL_INTEGRATION_CONFIGS.find((x) => x.id == _store.externalId)?.envs || [] + const envs = prepareVercelEvns(requiredEnvs, project as any) + + await Promise.allSettled( + envs.map(async (env: any) => { + try { + const data = await createVercelEnv({ + ...env, + vercelProjectId: _store.selectedVercelProject?.id, + vercelTeamId: _store.teamId, + vercelToken: _store.token, + }) + return data + } catch (err) { + console.error(`Error: ${err}`) + } + }) + ) + + const query = new URLSearchParams(_store.queryParams).toString() + router.push(`/vercel/complete?${query}`) + }, + }) + const canSubmit = projectName != '' && passwordStrengthScore >= DEFAULT_MINIMUM_PASSWORD_STRENGTH && @@ -193,67 +222,24 @@ const CreateProject = observer(() => { delayedCheckPasswordStrength(password) } - async function createSupabaseProject(dbSql: string) { - const data = { - cloud_provider: PROVIDERS.AWS.id, // hardcoded for DB instances to be under AWS - org_id: Number(_store.supabaseOrgId), - name: projectName, - db_pass: dbPass, - db_region: dbRegion, - db_sql: dbSql || '', - db_pricing_tier_id: PRICING_TIER_PRODUCT_IDS.FREE, - auth_site_url: _store.selectedVercelProjectUrl, - vercel_configuration_id: _store.configurationId, - } - const project = await post(`${API_URL}/projects`, data) - return { ...project, db_host: `db.${project.ref}.supabase.co`, db_password: dbPass } - } - async function onCreateProject() { - setLoading(true) - try { - const requiredEnvs = - VERCEL_INTEGRATION_CONFIGS.find((x) => x.id == _store.externalId)?.envs || [] const dbSql = VERCEL_INTEGRATION_CONFIGS.find((x) => x.id == _store.externalId)?.template?.sql || '' - const response = await createSupabaseProject(dbSql) - if (response.error) { - setLoading(false) - ui.setNotification({ - category: 'error', - message: `Failed to create project: ${response.error.message}`, - }) - return - } - - const project = response - _store.supabaseProjectRef = project.ref - - const envs = prepareVercelEvns(requiredEnvs, project) - - await Promise.allSettled( - envs.map(async (env: any) => { - try { - const data = await createVercelEnv({ - ...env, - vercelProjectId: _store.selectedVercelProject?.id, - vercelTeamId: _store.teamId, - vercelToken: _store.token, - }) - return data - } catch (err) { - console.error(`Error: ${err}`) - } - }) - ) - - const query = new URLSearchParams(_store.queryParams).toString() - router.push(`/vercel/complete?${query}`) + createProject({ + cloudProvider: PROVIDERS.AWS.id, + organizationId: Number(_store.supabaseOrgId), + name: projectName, + dbPass: dbPass, + dbRegion: dbRegion, + dbSql: dbSql || '', + dbPricingTierId: PRICING_TIER_PRODUCT_IDS.FREE, + configurationId: _store.configurationId, + authSiteUrl: _store.selectedVercelProjectUrl, + }) } catch (error) { console.error('Error', error) - setLoading(false) } } @@ -322,7 +308,7 @@ const CreateProject = observer(() => {
-