Paid plans come with one compute instance included. Additional projects will at
- least cost the compute instance hours used (min $7/month). See{' '}
+ least cost the compute instance hours used (min $10/month). See{' '}
Compute Instance Usage Billing
@@ -320,7 +346,7 @@ const MigrateOrganizationBillingButton = observer(() => {
)}
- {!migrationPreviewIsLoading && migrationPreviewData && dbTier !== 'tier_free' && (
+ {migrationPreviewIsSuccess && dbTier !== 'tier_free' && (
{
)}
+
+ The migration can take up to 30 seconds, please do not cancel the request or close
+ your browser.
+
+
+ {/* Show info when launching a new project in a paid org that has no project yet */}
+ {orgSubscription?.plan?.id !== 'free' && orgProjectCount === 0 && (
+
+
+ As this is the first project you're launching in this organization, it
+ comes with no additional compute costs.
+
+
+ )}
+
{/* Show info when launching a new project in a paid org that already has at least one project */}
{orgSubscription?.plan?.id !== 'free' && orgProjectCount > 0 && (
Launching another project incurs additional compute costs, starting at
- $0.01344 per hour (~$10/month).
-
-
- You can also create a new organization under the free plan (unless you
- have exceeded your 2 free project limit).
+ $0.01344 per hour (~$10/month). You can also create a new organization
+ under the free plan in case you have not exceeded your 2 free project
+ limit.
)}
diff --git a/studio/pages/new/index.tsx b/studio/pages/new/index.tsx
index be52ba18366a7..5a14b15fa9b0b 100644
--- a/studio/pages/new/index.tsx
+++ b/studio/pages/new/index.tsx
@@ -1,161 +1,106 @@
+import HCaptcha from '@hcaptcha/react-hcaptcha'
+import { Elements } from '@stripe/react-stripe-js'
+import { loadStripe } from '@stripe/stripe-js'
+import { useParams, useTheme } from 'common'
import { observer } from 'mobx-react-lite'
-import { useRouter } from 'next/router'
-import { useState } from 'react'
-import { Button, Input, Listbox } from 'ui'
+import { useCallback, useEffect, useState } from 'react'
+import { NewOrgForm } from 'components/interfaces/Organization'
import { WizardLayout } from 'components/layouts'
-import Panel from 'components/ui/Panel'
-import { useOrganizationCreateMutation } from 'data/organizations/organization-create-mutation'
-import { useStore } from 'hooks'
+import { useSetupIntent } from 'data/stripe/setup-intent-mutation'
+import { STRIPE_PUBLIC_KEY } from 'lib/constants'
+import { useIsHCaptchaLoaded } from 'stores/hcaptcha-loaded-store'
import { NextPageWithLayout } from 'types'
-const ORG_KIND_TYPES = {
- PERSONAL: 'Personal',
- EDUCATIONAL: 'Educational',
- STARTUP: 'Startup',
- AGENCY: 'Agency',
- COMPANY: 'Company',
- UNDISCLOSED: 'N/A',
-}
-const ORG_KIND_DEFAULT = 'PERSONAL'
-
-const ORG_SIZE_TYPES = {
- '1': '1 - 10',
- '10': '10 - 49',
- '50': '50 - 99',
- '100': '100 - 299',
- '300': 'More than 300',
-}
-const ORG_SIZE_DEFAULT = '1'
+const stripePromise = loadStripe(STRIPE_PUBLIC_KEY)
/**
* No org selected yet, create a new one
*/
const Wizard: NextPageWithLayout = () => {
- 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 { 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
- }
+ const { isDarkMode } = useTheme()
+
+ const [intent, setIntent] = useState
()
+ const captchaLoaded = useIsHCaptchaLoaded()
+
+ const [captchaToken, setCaptchaToken] = useState(null)
+ const [captchaRef, setCaptchaRef] = useState(null)
- function onOrgNameChange(e: any) {
- setOrgName(e.target.value)
+ const { mutate: setupIntent } = useSetupIntent({ onSuccess: (res) => setIntent(res) })
+
+ const captchaRefCallback = useCallback((node) => {
+ setCaptchaRef(node)
+ }, [])
+
+ const initSetupIntent = async (hcaptchaToken: string | undefined) => {
+ if (!hcaptchaToken) return console.error('Hcaptcha token is required')
+
+ // Force a reload of Elements, necessary for Stripe
+ setIntent(undefined)
+ setupIntent({ hcaptchaToken })
}
- function onOrgKindChange(value: any) {
- setOrgKind(value)
+ const options = {
+ clientSecret: intent ? intent.client_secret : '',
+ appearance: { theme: isDarkMode ? 'night' : 'flat', labels: 'floating' },
+ } as any
+
+ const loadPaymentForm = async () => {
+ if (captchaRef && captchaLoaded) {
+ let token = captchaToken
+
+ try {
+ if (!token) {
+ const captchaResponse = await captchaRef.execute({ async: true })
+ token = captchaResponse?.response ?? null
+ }
+ } catch (error) {
+ return
+ }
+
+ await initSetupIntent(token ?? undefined)
+ resetCaptcha()
+ }
}
- function onOrgSizeChange(value: any) {
- setOrgSize(value)
+ useEffect(() => {
+ loadPaymentForm()
+ }, [captchaRef, captchaLoaded])
+
+ const resetSetupIntent = () => {
+ return loadPaymentForm()
}
- async function onClickSubmit(e: any) {
- e.preventDefault()
- const trimmedOrgName = orgName ? orgName.trim() : ''
- const isOrgNameValid = validateOrgName(trimmedOrgName)
- if (!isOrgNameValid) {
- return ui.setNotification({ category: 'error', message: 'Organization name is empty' })
- }
+ const onLocalCancel = () => {
+ setIntent(undefined)
+ }
- createOrganization({
- name: trimmedOrgName,
- kind: orgKind,
- ...(orgKind == 'COMPANY' ? { size: orgSize } : {}),
- })
+ const resetCaptcha = () => {
+ setCaptchaToken(null)
+ captchaRef?.resetCaptcha()
}
return (
-
- Create a new organization
-
- }
- footer={
-