11import { useState } from 'react'
22import { useAuth } from '@/contexts/AuthContext'
33import { useNavigate , Link } from 'react-router-dom'
4+ import { motion } from 'framer-motion'
45import { Button } from '@/components/ui/button'
56import { Input } from '@/components/ui/input'
67import { Label } from '@/components/ui/label'
78import { Alert , AlertDescription } from '@/components/ui/alert'
89import { Navbar } from '@/components/landing'
9- import { Github , Loader2 , Mail , Lock } from 'lucide-react'
10+ import { Github , Loader2 , Mail , Lock , CheckCircle2 , Send , ArrowLeft } from 'lucide-react'
1011
1112export function SignupForm ( ) {
1213 const [ email , setEmail ] = useState ( '' )
@@ -15,9 +16,36 @@ export function SignupForm() {
1516 const [ error , setError ] = useState ( '' )
1617 const [ loading , setLoading ] = useState ( false )
1718 const [ oauthLoading , setOauthLoading ] = useState < 'github' | 'google' | null > ( null )
18- const { signUp, signInWithGitHub, signInWithGoogle } = useAuth ( )
19+ const [ emailSent , setEmailSent ] = useState ( false )
20+ const [ resendLoading , setResendLoading ] = useState ( false )
21+ const [ resendSuccess , setResendSuccess ] = useState ( false )
22+ const { signUp, signInWithGitHub, signInWithGoogle, resendVerification } = useAuth ( )
1923 const navigate = useNavigate ( )
2024
25+ const handleResend = async ( ) => {
26+ setError ( '' )
27+ setResendLoading ( true )
28+ setResendSuccess ( false )
29+ try {
30+ await resendVerification ( email )
31+ setResendSuccess ( true )
32+ setError ( '' )
33+ } catch ( err : any ) {
34+ setError ( err . message || 'Failed to resend verification email' )
35+ } finally {
36+ setResendLoading ( false )
37+ }
38+ }
39+
40+ const handleGoBack = ( ) => {
41+ setEmail ( '' )
42+ setPassword ( '' )
43+ setConfirmPassword ( '' )
44+ setEmailSent ( false )
45+ setResendSuccess ( false )
46+ setError ( '' )
47+ }
48+
2149 const handleSubmit = async ( e : React . FormEvent ) => {
2250 e . preventDefault ( )
2351 setError ( '' )
@@ -35,7 +63,9 @@ export function SignupForm() {
3563 setLoading ( true )
3664 try {
3765 await signUp ( email , password )
38- navigate ( '/dashboard' )
66+ setEmailSent ( true )
67+ setPassword ( '' )
68+ setConfirmPassword ( '' )
3969 } catch ( err : any ) {
4070 setError ( err . message || 'Signup failed' )
4171 } finally {
@@ -73,22 +103,150 @@ export function SignupForm() {
73103
74104 < div className = "flex-1 flex items-center justify-center px-6 py-12" >
75105 < div className = "w-full max-w-sm" >
76- < div className = "text-center mb-8" >
77- < h1 className = "text-2xl font-semibold text-foreground mb-2" >
78- Create your account
79- </ h1 >
80- < p className = "text-sm text-muted-foreground" >
81- Free for open source projects
82- </ p >
83- </ div >
106+ { /* Email Verification Sent */ }
107+ { emailSent ? (
108+ < motion . div
109+ initial = { { opacity : 0 , y : 20 } }
110+ animate = { { opacity : 1 , y : 0 } }
111+ className = "text-center"
112+ >
113+ { /* Animated Icon */ }
114+ < div className = "relative w-24 h-24 mx-auto mb-8" >
115+ { /* Outer glow ring */ }
116+ < motion . div
117+ initial = { { scale : 0.8 , opacity : 0 } }
118+ animate = { { scale : 1 , opacity : 1 } }
119+ transition = { { delay : 0.2 , duration : 0.5 } }
120+ className = "absolute inset-0 rounded-full bg-gradient-to-r from-primary/20 to-purple-500/20 blur-xl"
121+ />
122+ { /* Icon container */ }
123+ < motion . div
124+ initial = { { scale : 0 } }
125+ animate = { { scale : 1 } }
126+ transition = { { type : "spring" , stiffness : 200 , damping : 15 , delay : 0.1 } }
127+ className = "relative w-24 h-24 rounded-full bg-gradient-to-br from-primary/10 to-purple-500/10 border border-primary/30 flex items-center justify-center"
128+ >
129+ < motion . div
130+ initial = { { scale : 0 , rotate : - 180 } }
131+ animate = { { scale : 1 , rotate : 0 } }
132+ transition = { { type : "spring" , stiffness : 200 , damping : 12 , delay : 0.3 } }
133+ >
134+ < Send className = "w-10 h-10 text-primary" />
135+ </ motion . div >
136+ </ motion . div >
137+ </ div >
138+
139+ { /* Title */ }
140+ < motion . h1
141+ initial = { { opacity : 0 , y : 10 } }
142+ animate = { { opacity : 1 , y : 0 } }
143+ transition = { { delay : 0.4 } }
144+ className = "text-3xl font-bold text-foreground mb-3"
145+ >
146+ Check your inbox
147+ </ motion . h1 >
148+
149+ { /* Email display */ }
150+ < motion . div
151+ initial = { { opacity : 0 , y : 10 } }
152+ animate = { { opacity : 1 , y : 0 } }
153+ transition = { { delay : 0.5 } }
154+ className = "mb-8"
155+ >
156+ < p className = "text-muted-foreground mb-2" > We sent a verification link to</ p >
157+ < div className = "inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary/5 border border-primary/20" >
158+ < Mail className = "w-4 h-4 text-primary" />
159+ < span className = "font-mono text-sm text-foreground" > { email } </ span >
160+ </ div >
161+ { error && (
162+ < p className = "text-sm text-destructive mt-3" > { error } </ p >
163+ ) }
164+ </ motion . div >
165+
166+ { /* Instructions card */ }
167+ < motion . div
168+ initial = { { opacity : 0 , y : 10 } }
169+ animate = { { opacity : 1 , y : 0 } }
170+ transition = { { delay : 0.6 } }
171+ className = "bg-card/50 backdrop-blur-sm rounded-xl border border-border p-6 mb-6"
172+ >
173+ < div className = "flex items-start gap-4 text-left" >
174+ < div className = "w-8 h-8 rounded-lg bg-green-500/10 border border-green-500/20 flex items-center justify-center flex-shrink-0 mt-0.5" >
175+ < CheckCircle2 className = "w-4 h-4 text-green-500" />
176+ </ div >
177+ < div className = "flex-1" >
178+ < p className = "text-sm text-foreground font-medium mb-1" >
179+ Click the link in the email to verify your account
180+ </ p >
181+ < p className = "text-sm text-muted-foreground mb-3" >
182+ Didn't receive it? Check your spam folder.
183+ </ p >
184+
185+ { /* Resend success message */ }
186+ { resendSuccess && (
187+ < p className = "text-sm text-green-500 mb-3" >
188+ ✓ Verification email resent!
189+ </ p >
190+ ) }
191+
192+ { /* Action buttons */ }
193+ < div className = "flex items-center gap-3" >
194+ < button
195+ onClick = { handleResend }
196+ disabled = { resendLoading }
197+ className = "text-sm text-primary hover:underline font-medium disabled:opacity-50"
198+ >
199+ { resendLoading ? 'Sending...' : 'Resend email' }
200+ </ button >
201+ < span className = "text-muted-foreground" > ·</ span >
202+ < button
203+ onClick = { handleGoBack }
204+ disabled = { resendLoading }
205+ className = "text-sm text-muted-foreground hover:text-foreground disabled:opacity-50 disabled:cursor-not-allowed"
206+ >
207+ Use different email
208+ </ button >
209+ </ div >
210+ </ div >
211+ </ div >
212+ </ motion . div >
213+
214+ { /* Back link */ }
215+ < motion . div
216+ initial = { { opacity : 0 } }
217+ animate = { { opacity : 1 } }
218+ transition = { { delay : 0.7 } }
219+ >
220+ < Link
221+ to = "/login"
222+ className = { `inline-flex items-center gap-2 text-sm text-muted-foreground transition-colors ${
223+ resendLoading ? 'opacity-50 pointer-events-none' : 'hover:text-foreground'
224+ } `}
225+ onClick = { ( e ) => resendLoading && e . preventDefault ( ) }
226+ >
227+ < ArrowLeft className = "w-4 h-4" />
228+ Back to login
229+ </ Link >
230+ </ motion . div >
231+ </ motion . div >
232+ ) : (
233+ < >
234+ < div className = "text-center mb-8" >
235+ < h1 className = "text-2xl font-semibold text-foreground mb-2" >
236+ Create your account
237+ </ h1 >
238+ < p className = "text-sm text-muted-foreground" >
239+ Free for open source projects
240+ </ p >
241+ </ div >
84242
85- < div className = "bg-card rounded-lg border border-border p-6" >
86- < form onSubmit = { handleSubmit } className = "space-y-4" >
87- { error && (
88- < Alert variant = "destructive" className = "bg-destructive/10 border-destructive/20" >
89- < AlertDescription > { error } </ AlertDescription >
90- </ Alert >
91- ) }
243+ < div className = "bg-card rounded-lg border border-border p-6" >
244+ < form onSubmit = { handleSubmit } className = "space-y-4" >
245+ { error && (
246+ < Alert variant = "destructive" className = "bg-destructive/10 border-destructive/20" >
247+ < AlertDescription > { error } </ AlertDescription >
248+ </ Alert >
249+ ) }
92250
93251 < div className = "space-y-2" >
94252 < Label htmlFor = "email" > Email</ Label >
@@ -242,6 +400,8 @@ export function SignupForm() {
242400 Sign in
243401 </ Link >
244402 </ p >
403+ </ >
404+ ) }
245405 </ div >
246406 </ div >
247407 </ div >
0 commit comments