@@ -5,12 +5,12 @@ import { useRouter } from 'next/navigation'
55import { FiMail , FiLock , FiUser , FiLogIn , FiUserPlus , FiSun , FiMoon } from 'react-icons/fi'
66import { motion } from 'framer-motion'
77import Link from 'next/link'
8- import dynamic from " next/dynamic" ;
8+ import dynamic from ' next/dynamic'
99
1010const Turnstile = dynamic (
11- ( ) => import ( " @marsidev/react-turnstile" ) . then ( ( mod ) => mod . Turnstile ) ,
11+ ( ) => import ( ' @marsidev/react-turnstile' ) . then ( ( mod ) => mod . Turnstile ) ,
1212 { ssr : false }
13- ) ;
13+ )
1414
1515export default function LoginPage ( ) {
1616 const [ email , setEmail ] = useState ( '' )
@@ -38,17 +38,28 @@ export default function LoginPage() {
3838 const handleAuth = async ( ) => {
3939 setLoading ( true )
4040 setError ( '' )
41-
41+
4242 try {
4343 if ( isLogin ) {
44+ // Login with frontend anon key
4445 const { error } = await supabase . auth . signInWithPassword ( { email, password } )
4546 if ( error ) throw error
4647 router . push ( '/dashboard' )
4748 } else {
49+ // Signup via API route with Turnstile
4850 if ( ! captchaToken ) throw new Error ( 'Please complete captcha' )
49- const { error } = await supabase . auth . signUp ( { email, password, options : { captchaToken } } )
50- if ( error ) throw error
51- alert ( 'Check your email for confirmation!' )
51+
52+ const res = await fetch ( '/auth' , {
53+ method : 'POST' ,
54+ headers : { 'Content-Type' : 'application/json' } ,
55+ body : JSON . stringify ( { email, password, captchaToken, action : 'signup' } ) ,
56+ } )
57+
58+ const data = await res . json ( )
59+ if ( ! res . ok ) throw new Error ( data . message )
60+
61+ alert ( data . message )
62+ setIsLogin ( true ) // switch to login after signup
5263 }
5364 } catch ( err ) {
5465 setError ( err . message )
@@ -57,60 +68,47 @@ export default function LoginPage() {
5768 }
5869 }
5970
60- const handleGoogleSignIn = async ( ) => {
61- const { error } = await supabase . auth . signInWithOAuth ( {
62- provider : 'google' ,
63- } )
64- if ( error ) console . error ( 'Error signing in with Google:' , error . message )
71+ const handleGoogleSignIn = async ( ) => {
72+ const { error } = await supabase . auth . signInWithOAuth ( { provider : 'google' } )
73+ if ( error ) console . error ( 'Google sign-in error:' , error . message )
6574 }
6675
6776 return (
68- < div className = "min-h-screen dark:bg-gray-950 bg-gray-50 flex items-center justify-center p-4" >
69- < motion . div
77+ < div className = "min-h-screen flex items-center justify-center p-4 bg-gray-50 dark:bg-gray-950 " >
78+ < motion . div
7079 initial = { { opacity : 0 , y : 20 } }
7180 animate = { { opacity : 1 , y : 0 } }
7281 className = "w-full max-w-md bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden border border-gray-200 dark:border-gray-700"
7382 >
74- { /* Header with theme toggle */ }
83+ { /* Header */ }
7584 < div className = "bg-blue-600 p-6 text-white flex justify-between items-center" >
7685 < div >
7786 < h1 className = "text-2xl font-bold" > { isLogin ? 'Welcome Back' : 'Create Account' } </ h1 >
7887 < p className = "text-blue-100 dark:text-blue-200" >
7988 { isLogin ? 'Sign in to access your dashboard' : 'Join us to get started' }
8089 </ p >
8190 </ div >
82- < button
83- onClick = { toggleTheme }
84- className = "p-2 rounded-full hover:bg-blue-700 transition duration-200"
85- aria-label = "Toggle theme"
86- >
87- { theme === 'light' ? (
88- < FiMoon className = "w-5 h-5 text-white" />
89- ) : (
90- < FiSun className = "w-5 h-5 text-white" />
91- ) }
91+ < button onClick = { toggleTheme } className = "p-2 rounded-full hover:bg-blue-700" aria-label = "Toggle theme" >
92+ { theme === 'light' ? < FiMoon className = "w-5 h-5" /> : < FiSun className = "w-5 h-5" /> }
9293 </ button >
9394 </ div >
9495
9596 < div className = "p-6 space-y-4" >
9697 { error && (
97- < motion . div
98- initial = { { opacity : 0 } }
99- animate = { { opacity : 1 } }
100- className = "bg-red-100 dark:bg-red-900/30 border-l-4 border-red-500 text-red-700 dark:text-red-300 p-3 rounded"
101- >
102- < p > { error } </ p >
98+ < motion . div initial = { { opacity : 0 } } animate = { { opacity : 1 } } className = "bg-red-100 dark:bg-red-900/30 border-l-4 border-red-500 text-red-700 dark:text-red-300 p-3 rounded" >
99+ { error }
103100 </ motion . div >
104101 ) }
105102
103+ { /* Form */ }
106104 < div className = "space-y-4" >
107105 < div className = "relative" >
108106 < div className = "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none" >
109107 < FiMail className = "text-gray-400 dark:text-gray-500" />
110108 </ div >
111109 < input
112110 type = "email"
113- className = "w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-800 dark:text-gray-200"
111+ className = "w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-200"
114112 placeholder = "Email address"
115113 value = { email }
116114 onChange = { ( e ) => setEmail ( e . target . value ) }
@@ -123,7 +121,7 @@ export default function LoginPage() {
123121 </ div >
124122 < input
125123 type = "password"
126- className = "w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-800 dark:text-gray-200"
124+ className = "w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-200"
127125 placeholder = "Password"
128126 value = { password }
129127 onChange = { ( e ) => setPassword ( e . target . value ) }
@@ -137,7 +135,7 @@ export default function LoginPage() {
137135 </ div >
138136 < input
139137 type = "text"
140- className = "w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-800 dark:text-gray-200"
138+ className = "w-full pl-10 pr-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:text-gray-200"
141139 placeholder = "Full name (optional)"
142140 />
143141 </ div >
@@ -156,69 +154,55 @@ export default function LoginPage() {
156154 onClick = { handleAuth }
157155 disabled = { loading }
158156 className = { `w-full flex items-center justify-center py-3 px-4 rounded-lg text-white font-medium transition-all ${
159- loading
160- ? 'bg-gray-400 dark:bg-gray-600 cursor-not-allowed'
161- : 'bg-blue-600 hover:from -blue-700 hover:to-purple-700 dark:hover:from -blue-800 dark:hover:to-purple -800 shadow-md hover:shadow-lg'
157+ loading
158+ ? 'bg-gray-400 dark:bg-gray-600 cursor-not-allowed'
159+ : 'bg-blue-600 hover:bg -blue-700 dark:hover:bg -blue-800 shadow-md hover:shadow-lg'
162160 } `}
163161 >
164162 { loading ? (
165163 'Processing...'
164+ ) : isLogin ? (
165+ < >
166+ < FiLogIn className = "mr-2" /> Sign In
167+ </ >
166168 ) : (
167169 < >
168- { isLogin ? (
169- < >
170- < FiLogIn className = "mr-2" /> Sign In
171- </ >
172- ) : (
173- < >
174- < FiUserPlus className = "mr-2" /> Sign Up
175- </ >
176- ) }
170+ < FiUserPlus className = "mr-2" /> Sign Up
177171 </ >
178172 ) }
179173 </ button >
180174 </ div >
181175
176+ { /* Switch forms */ }
182177 < div className = "text-center text-sm text-gray-600 dark:text-gray-400" >
183178 { isLogin ? (
184179 < p >
185180 Don't have an account?{ ' ' }
186- < button
187- onClick = { ( ) => setIsLogin ( false ) }
188- className = "text-blue-600 dark:text-blue-400 hover:underline font-medium"
189- >
181+ < button onClick = { ( ) => setIsLogin ( false ) } className = "text-blue-600 dark:text-blue-400 hover:underline" >
190182 Sign up
191183 </ button >
192184 </ p >
193185 ) : (
194186 < p >
195187 Already have an account?{ ' ' }
196- < button
197- onClick = { ( ) => setIsLogin ( true ) }
198- className = "text-blue-600 dark:text-blue-400 hover:underline font-medium"
199- >
188+ < button onClick = { ( ) => setIsLogin ( true ) } className = "text-blue-600 dark:text-blue-400 hover:underline" >
200189 Sign in
201190 </ button >
202191 </ p >
203192 ) }
204193 </ div >
205194
195+ { /* Google OAuth */ }
206196 < div className = "relative flex items-center py-4" >
207197 < div className = "flex-grow border-t border-gray-300 dark:border-gray-600" > </ div >
208198 < span className = "flex-shrink mx-4 text-gray-500 dark:text-gray-400" > or</ span >
209199 < div className = "flex-grow border-t border-gray-300 dark:border-gray-600" > </ div >
210200 </ div >
211201
212202 < button
213- onClick = { ( ) => supabase . auth . signInWithOAuth ( { provider : 'google' } ) }
203+ onClick = { handleGoogleSignIn }
214204 className = "w-full flex items-center justify-center py-3 px-4 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-200 font-medium hover:bg-gray-50 dark:hover:bg-gray-600 transition-all"
215205 >
216- < svg className = "w-5 h-5 mr-2" viewBox = "0 0 24 24" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
217- < path d = "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill = "#4285F4" />
218- < path d = "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill = "#34A853" />
219- < path d = "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill = "#FBBC05" />
220- < path d = "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill = "#EA4335" />
221- </ svg >
222206 Continue with Google
223207 </ button >
224208
0 commit comments