diff --git a/README.md b/README.md
index 1f45dcd63a..bc55f10d57 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-
+
-
+
diff --git a/sim/app/(auth)/signup/signup-form.tsx b/sim/app/(auth)/signup/signup-form.tsx
index 5197f525fd..04551454c3 100644
--- a/sim/app/(auth)/signup/signup-form.tsx
+++ b/sim/app/(auth)/signup/signup-form.tsx
@@ -1,8 +1,8 @@
'use client'
-import { useEffect, useState } from 'react'
+import { Suspense, useEffect, useState } from 'react'
import Link from 'next/link'
-import { useRouter } from 'next/navigation'
+import { useRouter, useSearchParams } from 'next/navigation'
import { Eye, EyeOff } from 'lucide-react'
import { Button } from '@/components/ui/button'
import {
@@ -37,7 +37,7 @@ const PASSWORD_VALIDATIONS = {
},
}
-export default function SignupPage({
+function SignupFormContent({
githubAvailable,
googleAvailable,
isProduction,
@@ -47,6 +47,7 @@ export default function SignupPage({
isProduction: boolean
}) {
const router = useRouter()
+ const searchParams = useSearchParams()
const [isLoading, setIsLoading] = useState(false)
const [, setMounted] = useState(false)
const { addNotification } = useNotificationStore()
@@ -54,10 +55,15 @@ export default function SignupPage({
const [password, setPassword] = useState('')
const [passwordErrors, setPasswordErrors] = useState([])
const [showValidationError, setShowValidationError] = useState(false)
+ const [email, setEmail] = useState('')
useEffect(() => {
setMounted(true)
- }, [])
+ const emailParam = searchParams.get('email')
+ if (emailParam) {
+ setEmail(emailParam)
+ }
+ }, [searchParams])
// Validate password and return array of error messages
const validatePassword = (passwordValue: string): string[] => {
@@ -100,7 +106,7 @@ export default function SignupPage({
setIsLoading(true)
const formData = new FormData(e.currentTarget)
- const email = formData.get('email') as string
+ const emailValue = formData.get('email') as string
const passwordValue = formData.get('password') as string
const name = formData.get('name') as string
@@ -121,7 +127,7 @@ export default function SignupPage({
const response = await client.signUp.email(
{
- email,
+ email: emailValue,
password: passwordValue,
name,
},
@@ -169,7 +175,7 @@ export default function SignupPage({
}
if (typeof window !== 'undefined') {
- sessionStorage.setItem('verificationEmail', email)
+ sessionStorage.setItem('verificationEmail', emailValue)
}
router.push(`/verify?fromSignup=true`)
@@ -221,6 +227,8 @@ export default function SignupPage({
type="email"
placeholder="name@example.com"
required
+ value={email}
+ onChange={(e) => setEmail(e.target.value)}
/>
@@ -275,3 +283,29 @@ export default function SignupPage({
)
}
+
+export default function SignupPage({
+ githubAvailable,
+ googleAvailable,
+ isProduction,
+}: {
+ githubAvailable: boolean
+ googleAvailable: boolean
+ isProduction: boolean
+}) {
+ return (
+
+
+
+ }
+ >
+
+
+ )
+}
diff --git a/sim/app/(landing)/components/nav-client.tsx b/sim/app/(landing)/components/nav-client.tsx
index 8dc4ea6174..0c881c33fe 100644
--- a/sim/app/(landing)/components/nav-client.tsx
+++ b/sim/app/(landing)/components/nav-client.tsx
@@ -59,7 +59,7 @@ export default function NavClient({ children }: { children: React.ReactNode }) {
('idle')
+ const [status, setStatus] = useState<'idle' | 'success' | 'error' | 'exists' | 'ratelimited'>(
+ 'idle'
+ )
+ const [errorMessage, setErrorMessage] = useState('')
+ const [retryAfter, setRetryAfter] = useState(null)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setStatus('idle')
+ setErrorMessage('')
+ setRetryAfter(null)
try {
// Validate email
@@ -32,7 +38,20 @@ export default function WaitlistForm() {
const data = await response.json()
if (!response.ok) {
- setStatus('error')
+ // Check for rate limiting (429 status)
+ if (response.status === 429) {
+ setStatus('ratelimited')
+ setErrorMessage(data.message || 'Too many attempts. Please try again later.')
+ setRetryAfter(data.retryAfter || 60)
+ }
+ // Check if the error is because the email already exists
+ else if (response.status === 400 && data.message?.includes('already exists')) {
+ setStatus('exists')
+ setErrorMessage('Already on the waitlist')
+ } else {
+ setStatus('error')
+ setErrorMessage(data.message || 'Failed to join waitlist')
+ }
return
}
@@ -40,6 +59,7 @@ export default function WaitlistForm() {
setEmail('')
} catch (error) {
setStatus('error')
+ setErrorMessage('Please try again')
} finally {
setIsSubmitting(false)
}
@@ -49,9 +69,26 @@ export default function WaitlistForm() {
if (isSubmitting) return 'Joining...'
if (status === 'success') return 'Joined!'
if (status === 'error') return 'Try again'
+ if (status === 'exists') return 'Already joined'
+ if (status === 'ratelimited') return `Try again later`
return 'Join waitlist'
}
+ const getButtonStyle = () => {
+ switch (status) {
+ case 'success':
+ return 'bg-green-500 hover:bg-green-600'
+ case 'error':
+ return 'bg-red-500 hover:bg-red-600'
+ case 'exists':
+ return 'bg-amber-500 hover:bg-amber-600'
+ case 'ratelimited':
+ return 'bg-gray-500 hover:bg-gray-600'
+ default:
+ return 'bg-white text-black hover:bg-gray-100'
+ }
+ }
+
return (