@@ -15,7 +15,6 @@ import {
1515 CInputGroupText ,
1616 CFormInput ,
1717 CButton ,
18- CButtonGroup ,
1918 CTooltip ,
2019 CSpinner ,
2120} from '@coreui/react'
@@ -25,6 +24,8 @@ import { useStateContext } from '../../../context/contextProvider'
2524const Login = ( ) => {
2625 const [ username , setUsername ] = useState ( '' )
2726 const [ password , setPassword ] = useState ( '' )
27+ const [ code , setCode ] = useState ( '' )
28+ const [ step , setStep ] = useState ( 1 )
2829 const [ isPasswordVisible , setIsPasswordVisible ] = useState ( false )
2930 const [ errorMessage , setErrorMessage ] = useState ( null )
3031 const [ isLoading , setIsLoading ] = useState ( false )
@@ -38,33 +39,35 @@ const Login = () => {
3839
3940 try {
4041 const response = await axios . post ( '/login' , { username, password } )
41- const { token, user } = response . data
4242
43- if ( token && user ?. userRole ) {
44- // Store token and user info
45- sessionStorage . setItem ( 'token' , token )
46- sessionStorage . setItem ( 'user' , JSON . stringify ( user ) )
43+ // Proceed to 2FA step
44+ setStep ( 2 )
45+ } catch ( err ) {
46+ console . error ( err )
47+ setErrorMessage ( err . response ?. data ?. message || 'Login failed.' )
48+ } finally {
49+ setIsLoading ( false )
50+ }
51+ }
4752
48- // Notify app
49- window . dispatchEvent ( new Event ( 'storage' ) )
53+ const handle2FAVerification = async ( e ) => {
54+ e . preventDefault ( )
55+ setIsLoading ( true )
56+ setErrorMessage ( null )
5057
51- // Set global user (if you're using context)
52- setUser ( user )
58+ try {
59+ const response = await axios . post ( '/verify-2fa' , { username, code } )
60+ const { token, user } = response . data
5361
54- // Redirect based on role
55- if ( user . userRole === 'admin' ) {
56- navigate ( '/admindashboard' )
57- } else if ( user . userRole === 'user' ) {
58- navigate ( '/' )
59- } else {
60- setErrorMessage ( 'Unknown role. Please contact support.' )
61- }
62- } else {
63- setErrorMessage ( 'Invalid credentials or user role not found.' )
64- }
62+ sessionStorage . setItem ( 'token' , token )
63+ sessionStorage . setItem ( 'user' , JSON . stringify ( user ) )
64+ window . dispatchEvent ( new Event ( 'storage' ) )
65+ setUser ( user )
66+
67+ navigate ( user . userRole === 'admin' ? '/admindashboard' : '/' )
6568 } catch ( err ) {
6669 console . error ( err )
67- setErrorMessage ( 'Login failed. Please try again .')
70+ setErrorMessage ( err . response ?. data ?. message || '2FA verification failed .')
6871 } finally {
6972 setIsLoading ( false )
7073 }
@@ -88,81 +91,78 @@ const Login = () => {
8891 { errorMessage }
8992 </ CAlert >
9093 ) }
91- < CForm onSubmit = { handleLogin } >
92- < h1 > Login</ h1 >
94+
95+ < CForm onSubmit = { step === 1 ? handleLogin : handle2FAVerification } >
96+ < h1 > { step === 1 ? 'Login' : '2FA Verification' } </ h1 >
9397 < p className = "text-body-secondary" > Sign in to your account</ p >
94- < CInputGroup className = "mb-3" >
95- < CInputGroupText >
96- < FontAwesomeIcon icon = { faUser } />
97- </ CInputGroupText >
98- < CFormInput
99- type = "text"
100- placeholder = "Username"
101- autoComplete = "username"
102- value = { username }
103- onChange = { ( e ) => setUsername ( e . target . value ) }
104- required
105- />
106- </ CInputGroup >
107- < CInputGroup className = "mb-3" >
108- < CInputGroupText >
109- < FontAwesomeIcon icon = { faLock } />
110- </ CInputGroupText >
111- < CFormInput
112- type = { isPasswordVisible ? 'text' : 'password' }
113- placeholder = "Password"
114- autoComplete = "current-password"
115- value = { password }
116- onChange = { ( e ) => setPassword ( e . target . value ) }
117- required
118- />
119- < CInputGroupText >
120- < CTooltip
121- content = { isPasswordVisible ? 'Hide password' : 'Show password' }
122- placement = "top"
123- >
124- < span onClick = { ( ) => setIsPasswordVisible ( ! isPasswordVisible ) } >
125- < FontAwesomeIcon icon = { isPasswordVisible ? faEyeSlash : faEye } />
126- </ span >
127- </ CTooltip >
128- </ CInputGroupText >
129- </ CInputGroup >
130- < p >
131- < small >
132- By continuing, you agree to our
133- < a href = "/policy" className = "text-primary" >
134- { ' ' }
135- Privacy Policy{ ' ' }
136- </ a >
137- and
138- < a href = "/terms" className = "text-primary" >
139- { ' ' }
140- Terms of Service
141- </ a >
142- .
143- </ small >
144- </ p >
98+
99+ { step === 1 ? (
100+ < >
101+ < CInputGroup className = "mb-3" >
102+ < CInputGroupText >
103+ < FontAwesomeIcon icon = { faUser } />
104+ </ CInputGroupText >
105+ < CFormInput
106+ type = "text"
107+ placeholder = "Username"
108+ autoComplete = "username"
109+ value = { username }
110+ onChange = { ( e ) => setUsername ( e . target . value ) }
111+ required
112+ />
113+ </ CInputGroup >
114+
115+ < CInputGroup className = "mb-3" >
116+ < CInputGroupText >
117+ < FontAwesomeIcon icon = { faLock } />
118+ </ CInputGroupText >
119+ < CFormInput
120+ type = { isPasswordVisible ? 'text' : 'password' }
121+ placeholder = "Password"
122+ autoComplete = "current-password"
123+ value = { password }
124+ onChange = { ( e ) => setPassword ( e . target . value ) }
125+ required
126+ />
127+ < CInputGroupText >
128+ < CTooltip
129+ content = { isPasswordVisible ? 'Hide password' : 'Show password' }
130+ placement = "top"
131+ >
132+ < span onClick = { ( ) => setIsPasswordVisible ( ! isPasswordVisible ) } >
133+ < FontAwesomeIcon icon = { isPasswordVisible ? faEyeSlash : faEye } />
134+ </ span >
135+ </ CTooltip >
136+ </ CInputGroupText >
137+ </ CInputGroup >
138+ </ >
139+ ) : (
140+ < CInputGroup className = "mb-3" >
141+ < CInputGroupText > 🔐</ CInputGroupText >
142+ < CFormInput
143+ type = "text"
144+ placeholder = "Enter 2FA Code"
145+ value = { code }
146+ onChange = { ( e ) => setCode ( e . target . value ) }
147+ required
148+ />
149+ </ CInputGroup >
150+ ) }
151+
145152 < div className = "d-grid mb-3" >
146- < CButtonGroup >
147- < CButton type = "submit" color = "primary" className = "rounded" >
148- Login
149- </ CButton >
150- < CButton
151- color = "primary"
152- className = "rounded"
153- onClick = { ( ) => navigate ( '/signup' ) }
154- >
155- Signup
156- </ CButton >
157- </ CButtonGroup >
153+ < CButton type = "submit" color = "primary" className = "rounded" >
154+ { step === 1 ? 'Login' : 'Verify Code' }
155+ </ CButton >
158156 </ div >
159- < CButton
160- color = "link"
161- className = "px-0 text-primary"
162- onClick = { ( ) => console . log ( 'Forgot password clicked' ) }
163- >
164- Forgot password?
165- </ CButton >
157+
158+ { step === 1 && (
159+ < p >
160+ { "Don't have an account?" } { ' ' }
161+ < a href = "/signup" className = "signup-link" >
162+ Signup
163+ </ a >
164+ </ p >
165+ ) }
166166 </ CForm >
167167 </ CCardBody >
168168 </ CCard >
0 commit comments