1- 'use client'
2- import { useState , useEffect } from 'react'
3- import { supabase } from '../../lib/supabase'
4- import { useRouter } from 'next/navigation'
5- import { FiMail , FiLock , FiUser , FiLogIn , FiUserPlus , FiSun , FiMoon } from 'react-icons/fi'
6- import { motion } from 'framer-motion'
7- import Link from 'next/link'
8- import dynamic from 'next/dynamic'
1+ "use client" ;
2+ import { useState , useEffect } from "react" ;
3+ import { supabase } from "../../lib/supabase" ;
4+ import { useRouter } from "next/navigation" ;
5+ import {
6+ FiMail ,
7+ FiLock ,
8+ FiUser ,
9+ FiLogIn ,
10+ FiUserPlus ,
11+ FiSun ,
12+ FiMoon ,
13+ } from "react-icons/fi" ;
14+ import { motion } from "framer-motion" ;
15+ import Link from "next/link" ;
16+ import dynamic from "next/dynamic" ;
917
1018const Turnstile = dynamic (
11- ( ) => import ( ' @marsidev/react-turnstile' ) . then ( ( mod ) => mod . Turnstile ) ,
19+ ( ) => import ( " @marsidev/react-turnstile" ) . then ( ( mod ) => mod . Turnstile ) ,
1220 { ssr : false }
13- )
21+ ) ;
1422
1523export default function LoginPage ( ) {
16- const [ email , setEmail ] = useState ( '' )
17- const [ password , setPassword ] = useState ( '' )
18- const [ isLogin , setIsLogin ] = useState ( true )
19- const [ loading , setLoading ] = useState ( false )
20- const [ error , setError ] = useState ( '' )
21- const [ theme , setTheme ] = useState ( 'light' )
22- const [ captchaToken , setCaptchaToken ] = useState ( null )
23- const router = useRouter ( )
24+ const [ email , setEmail ] = useState ( "" ) ;
25+ const [ password , setPassword ] = useState ( "" ) ;
26+ const [ name , setName ] = useState ( "" ) ;
27+ const [ isLogin , setIsLogin ] = useState ( true ) ;
28+ const [ loading , setLoading ] = useState ( false ) ;
29+ const [ error , setError ] = useState ( "" ) ;
30+ const [ theme , setTheme ] = useState ( "light" ) ;
31+ const [ captchaToken , setCaptchaToken ] = useState ( null ) ;
32+ const router = useRouter ( ) ;
2433
2534 useEffect ( ( ) => {
26- const savedTheme = localStorage . getItem ( ' theme' ) || ' light'
27- setTheme ( savedTheme )
28- document . documentElement . classList . toggle ( ' dark' , savedTheme === ' dark' )
29- } , [ ] )
35+ const savedTheme = localStorage . getItem ( " theme" ) || " light" ;
36+ setTheme ( savedTheme ) ;
37+ document . documentElement . classList . toggle ( " dark" , savedTheme === " dark" ) ;
38+ } , [ ] ) ;
3039
3140 const toggleTheme = ( ) => {
32- const newTheme = theme === ' light' ? ' dark' : ' light'
33- setTheme ( newTheme )
34- localStorage . setItem ( ' theme' , newTheme )
35- document . documentElement . classList . toggle ( ' dark' , newTheme === ' dark' )
36- }
41+ const newTheme = theme === " light" ? " dark" : " light" ;
42+ setTheme ( newTheme ) ;
43+ localStorage . setItem ( " theme" , newTheme ) ;
44+ document . documentElement . classList . toggle ( " dark" , newTheme === " dark" ) ;
45+ } ;
3746
3847 const handleAuth = async ( ) => {
39- setLoading ( true )
40- setError ( '' )
48+ setLoading ( true ) ;
49+ setError ( "" ) ;
4150
4251 try {
43- if ( ! captchaToken ) throw new Error ( ' Please complete captcha' )
52+ if ( ! captchaToken ) throw new Error ( " Please complete captcha" ) ;
4453
4554 if ( isLogin ) {
4655 // Verify captcha first via API route
47- const verifyRes = await fetch ( '/api/auth' , {
48- method : 'POST' ,
49- headers : { 'Content-Type' : 'application/json' } ,
50- body : JSON . stringify ( { email, password, captchaToken, action : 'login' } ) ,
51- } )
52- const verifyData = await verifyRes . json ( )
53- if ( ! verifyData . success ) throw new Error ( verifyData . message || 'Captcha verification failed' )
56+ const verifyRes = await fetch ( "/api/auth" , {
57+ method : "POST" ,
58+ headers : { "Content-Type" : "application/json" } ,
59+ body : JSON . stringify ( {
60+ email,
61+ password,
62+ captchaToken,
63+ action : "login" ,
64+ } ) ,
65+ } ) ;
66+ const verifyData = await verifyRes . json ( ) ;
67+ if ( ! verifyData . success )
68+ throw new Error ( verifyData . message || "Captcha verification failed" ) ;
5469
5570 // After captcha verified, login using frontend anon key
56- const { error } = await supabase . auth . signInWithPassword ( { email, password } )
57- if ( error ) throw error
71+ const { error } = await supabase . auth . signInWithPassword ( {
72+ email,
73+ password,
74+ } ) ;
75+ if ( error ) throw error ;
5876
59- router . push ( ' /dashboard' )
77+ router . push ( " /dashboard" ) ;
6078 } else {
6179 // Signup flow remains the same
62- const res = await fetch ( '/api/auth' , {
63- method : 'POST' ,
64- headers : { 'Content-Type' : 'application/json' } ,
65- body : JSON . stringify ( { email, password, captchaToken, action : 'signup' } ) ,
66- } )
67- const data = await res . json ( )
68- if ( ! data . success ) throw new Error ( data . message || 'Signup failed' )
69- alert ( data . message )
70- setIsLogin ( true )
80+ const res = await fetch ( "/api/auth" , {
81+ method : "POST" ,
82+ headers : { "Content-Type" : "application/json" } ,
83+ body : JSON . stringify ( {
84+ email,
85+ password,
86+ captchaToken,
87+ action : "signup" ,
88+ name,
89+ } ) ,
90+ } ) ;
91+ const data = await res . json ( ) ;
92+ if ( ! data . success ) throw new Error ( data . message || "Signup failed" ) ;
93+ alert ( data . message ) ;
94+ setIsLogin ( true ) ;
7195 }
7296 } catch ( err ) {
73- setError ( err . message || ' Something went wrong' )
97+ setError ( err . message || " Something went wrong" ) ;
7498 } finally {
75- setLoading ( false )
99+ setLoading ( false ) ;
76100 }
77- }
101+ } ;
78102
79103 const handleGoogleSignIn = async ( ) => {
80- const { error } = await supabase . auth . signInWithOAuth ( { provider : 'google' } )
81- if ( error ) console . error ( 'Google sign-in error:' , error . message )
82- }
104+ const { error } = await supabase . auth . signInWithOAuth ( {
105+ provider : "google" ,
106+ } ) ;
107+ if ( error ) console . error ( "Google sign-in error:" , error . message ) ;
108+ } ;
83109
84110 return (
85111 < div className = "min-h-screen flex items-center justify-center p-4 bg-white dark:bg-neutral-900" >
@@ -91,19 +117,54 @@ export default function LoginPage() {
91117 { /* Header */ }
92118 < div className = "bg-blue-500 p-6 text-white flex justify-between items-center" >
93119 < div >
94- < h1 className = "text-2xl font-bold" > { isLogin ? 'Welcome Back' : 'Create Account' } </ h1 >
120+ < h1 className = "text-2xl font-bold" >
121+ { isLogin ? "Welcome Back" : "Create Account" }
122+ </ h1 >
95123 < p className = "text-blue-100 dark:text-blue-200" >
96- { isLogin ? 'Sign in to access your dashboard' : 'Join us to get started' }
124+ { isLogin
125+ ? "Sign in to access your dashboard"
126+ : "Join us to get started" }
97127 </ p >
98128 </ div >
99- < button onClick = { toggleTheme } className = "p-2 rounded-full hover:bg-blue-700" aria-label = "Toggle theme" >
100- { theme === 'light' ? < FiMoon className = "w-5 h-5" /> : < FiSun className = "w-5 h-5" /> }
129+ < button
130+ onClick = { toggleTheme }
131+ className = "p-2 rounded-full hover:bg-blue-700"
132+ aria-label = "Toggle theme"
133+ >
134+ { theme === "light" ? (
135+ < FiMoon className = "w-5 h-5" />
136+ ) : (
137+ < FiSun className = "w-5 h-5" />
138+ ) }
101139 </ button >
102140 </ div >
103141
142+ < div className = "flex justify-center items-center p-6" >
143+ { /* Google OAuth */ }
144+ < button
145+ onClick = { handleGoogleSignIn }
146+ 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-neutral-950 text-gray-700 dark:text-gray-200 font-medium hover:bg-gray-50 dark:hover:bg-neutral-900 duration-300 transition-all"
147+ >
148+ < img src = "./google.webp" width = { 24 } > </ img >
149+ < span className = "mx-2" > Continue with Google</ span >
150+ </ button >
151+ </ div >
152+
153+ < div className = "relative flex items-center px-6" >
154+ < div className = "flex-grow border-t border-gray-300 dark:border-gray-600" > </ div >
155+ < span className = "flex-shrink mx-4 text-gray-500 dark:text-gray-400" >
156+ or
157+ </ span >
158+ < div className = "flex-grow border-t border-gray-300 dark:border-gray-600" > </ div >
159+ </ div >
160+
104161 < div className = "p-6 space-y-4" >
105162 { error && (
106- < 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" >
163+ < motion . div
164+ initial = { { opacity : 0 } }
165+ animate = { { opacity : 1 } }
166+ 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"
167+ >
107168 { error }
108169 </ motion . div >
109170 ) }
@@ -145,6 +206,8 @@ export default function LoginPage() {
145206 type = "text"
146207 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-neutral-950 dark:text-gray-200"
147208 placeholder = "Full name"
209+ value = { name }
210+ onChange = { ( e ) => setName ( e . target . value ) }
148211 />
149212 </ div >
150213 ) }
@@ -162,19 +225,19 @@ export default function LoginPage() {
162225 disabled = { loading }
163226 className = { `w-full flex items-center justify-center py-3 px-4 rounded-lg text-white font-medium transition-all ${
164227 loading
165- ? ' bg-gray-400 dark:bg-gray-600 cursor-not-allowed'
166- : ' bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-800 shadow-md hover:shadow-lg'
228+ ? " bg-gray-400 dark:bg-gray-600 cursor-not-allowed"
229+ : " bg-blue-600 hover:bg-blue-700 dark:hover:bg-blue-800 shadow-md hover:shadow-lg"
167230 } `}
168231 >
169232 { loading ? (
170- ' Processing...'
233+ " Processing..."
171234 ) : isLogin ? (
172235 < >
173- < FiLogIn className = "mr-2" /> Sign In
236+ < FiLogIn className = "mr-2" /> Continue
174237 </ >
175238 ) : (
176239 < >
177- < FiUserPlus className = "mr-2" /> Sign Up
240+ < FiUserPlus className = "mr-2" /> Continue
178241 </ >
179242 ) }
180243 </ button >
@@ -184,48 +247,45 @@ export default function LoginPage() {
184247 < div className = "text-center text-sm text-gray-600 dark:text-gray-400" >
185248 { isLogin ? (
186249 < p >
187- Don't have an account?{ ' ' }
188- < button onClick = { ( ) => setIsLogin ( false ) } className = "text-blue-600 dark:text-blue-400 hover:underline" >
250+ Don't have an account?{ " " }
251+ < button
252+ onClick = { ( ) => setIsLogin ( false ) }
253+ className = "text-blue-600 dark:text-blue-400 hover:underline"
254+ >
189255 Sign up
190256 </ button >
191257 </ p >
192258 ) : (
193259 < p >
194- Already have an account?{ ' ' }
195- < button onClick = { ( ) => setIsLogin ( true ) } className = "text-blue-600 dark:text-blue-400 hover:underline" >
260+ Already have an account?{ " " }
261+ < button
262+ onClick = { ( ) => setIsLogin ( true ) }
263+ className = "text-blue-600 dark:text-blue-400 hover:underline"
264+ >
196265 Sign in
197266 </ button >
198267 </ p >
199268 ) }
200269 </ div >
201270
202- { /* Google OAuth */ }
203- < div className = "relative flex items-center py-4" >
204- < div className = "flex-grow border-t border-gray-300 dark:border-gray-600" > </ div >
205- < span className = "flex-shrink mx-4 text-gray-500 dark:text-gray-400" > or</ span >
206- < div className = "flex-grow border-t border-gray-300 dark:border-gray-600" > </ div >
207- </ div >
208-
209- < button
210- onClick = { handleGoogleSignIn }
211- 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-neutral-950 text-gray-700 dark:text-gray-200 font-medium hover:bg-gray-50 dark:hover:bg-neutral-900 duration-300 transition-all"
212- >
213- < img src = "./google.webp" width = { 24 } > </ img >
214- < span className = 'mx-2' > Continue with Google</ span >
215- </ button >
216-
217271 < div className = "text-center text-xs text-gray-500 dark:text-gray-400 mt-6" >
218- By continuing, you agree to our{ ' ' }
219- < Link href = "/terms" className = "text-blue-500 dark:text-blue-400 hover:underline" >
272+ By continuing, you agree to our{ " " }
273+ < Link
274+ href = "/terms"
275+ className = "text-blue-500 dark:text-blue-400 hover:underline"
276+ >
220277 Terms of Service
221- </ Link > { ' ' }
222- and{ ' ' }
223- < Link href = "/privacy" className = "text-blue-500 dark:text-blue-400 hover:underline" >
278+ </ Link > { " " }
279+ and{ " " }
280+ < Link
281+ href = "/privacy"
282+ className = "text-blue-500 dark:text-blue-400 hover:underline"
283+ >
224284 Privacy Policy
225285 </ Link >
226286 </ div >
227287 </ div >
228288 </ motion . div >
229289 </ div >
230- )
231- }
290+ ) ;
291+ }
0 commit comments