@@ -2,19 +2,20 @@ import React, { useState, useEffect } from "react";
22import { Link } from "react-router-dom" ;
33import { supabase } from "../lib/supabaseClient" ;
44import { BrainCircuit , Mail , ArrowLeft , Loader2 , CheckCircle2 , Lock , KeyRound , AlertCircle } from "lucide-react" ;
5-
5+
66import { motion , AnimatePresence } from "framer-motion" ;
7+ import validatePassword from "@/utils/validatePassword" ;
78
89function ForgotPassword ( ) {
9- const [ step , setStep ] = useState ( 1 ) ;
10- const [ email , setEmail ] = useState ( '' ) ;
11- const [ otp , setOtp ] = useState ( '' ) ;
12- const [ newPassword , setNewPassword ] = useState ( '' ) ;
13- const [ loading , setLoading ] = useState ( false ) ;
14- const [ message , setMessage ] = useState ( '' ) ;
15- const [ error , setError ] = useState ( '' ) ;
16- const [ timeLeft , setTimeLeft ] = useState ( 900 ) ;
17- const [ timerExpired , setTimerExpired ] = useState ( false ) ;
10+ const [ step , setStep ] = useState ( 1 ) ;
11+ const [ email , setEmail ] = useState ( '' ) ;
12+ const [ otp , setOtp ] = useState ( '' ) ;
13+ const [ newPassword , setNewPassword ] = useState ( '' ) ;
14+ const [ loading , setLoading ] = useState ( false ) ;
15+ const [ message , setMessage ] = useState ( '' ) ;
16+ const [ error , setError ] = useState ( '' ) ;
17+ const [ timeLeft , setTimeLeft ] = useState ( 900 ) ;
18+ const [ timerExpired , setTimerExpired ] = useState ( false ) ;
1819
1920 useEffect ( ( ) => {
2021 if ( step !== 2 ) return ;
@@ -46,79 +47,80 @@ function ForgotPassword() {
4647 return ;
4748 }
4849 return prev - 1 ;
49- } ) ;
50- } , 1000 ) ;
51- return ( ) => clearInterval ( timer ) ;
50+ } ) ;
51+ } , 1000 ) ;
52+ return ( ) => clearInterval ( timer ) ;
5253 } , [ step ] ) ;
5354
54- const formatTime = ( seconds ) => {
55+ const formatTime = ( seconds ) => {
5556 const mins = Math . floor ( seconds / 60 ) ;
5657 const secs = seconds % 60 ;
5758 return `${ mins } :${ secs . toString ( ) . padStart ( 2 , '0' ) } ` ;
58- } ;
59+ } ;
5960
60- try {
61- const { error } = await supabase . auth . resetPasswordForEmail ( email ) ;
62- if ( error ) throw error ;
63- setMessage ( "Check your email for the 6-digit recovery code!" ) ;
64- setStep ( 2 ) ;
65- } catch ( err ) {
66- console . error ( "Password reset error:" , err ) ;
67- setError ( err . message || "An error occurred. Please try again." ) ;
68- } finally {
69- setLoading ( false ) ;
70- }
61+ try {
62+ const { error } = await supabase . auth . resetPasswordForEmail ( email ) ;
63+ if ( error ) throw error ;
64+ setMessage ( "Check your email for the 6-digit recovery code!" ) ;
65+ setStep ( 2 ) ;
66+ } catch ( err ) {
67+ console . error ( "Password reset error:" , err ) ;
68+ setError ( err . message || "An error occurred. Please try again." ) ;
69+ } finally {
70+ setLoading ( false ) ;
71+ }
7172 } ;
7273
73- if ( error ) throw error ;
74+ if ( error ) throw error ;
7475
75- setMessage ( 'Check your email for the 6-digit recovery code!' ) ;
76- setStep ( 2 ) ;
76+ setMessage ( 'Check your email for the 6-digit recovery code!' ) ;
77+ setStep ( 2 ) ;
7778 } catch ( err ) {
78- console . error ( 'Password reset error:' , err ) ;
79- setError ( err . message || 'An error occurred. Please try again.' ) ;
80- } finally {
81- setLoading ( false ) ;
82- }
79+ console . error ( 'Password reset error:' , err ) ;
80+ setError ( err . message || 'An error occurred. Please try again.' ) ;
81+ } finally {
82+ setLoading ( false ) ;
83+ }
8384 } ;
8485
85- const handleVerifyOtp = async ( e ) => {
86+ const handleVerifyOtp = async ( e ) => {
8687 e . preventDefault ( ) ;
8788 if ( ! otp || otp . length !== 6 ) {
88- setError ( 'Please enter the 6-digit code' ) ;
89- return ;
89+ setError ( 'Please enter the 6-digit code' ) ;
90+ return ;
9091 }
9192 if ( timerExpired ) {
92- setError ( 'Your code has expired. Please request a new one.' ) ;
93- return ;
93+ setError ( 'Your code has expired. Please request a new one.' ) ;
94+ return ;
9495 }
9596
9697 setLoading ( true ) ;
9798 setError ( '' ) ;
9899
99100 try {
100- const { error } = await supabase . auth . verifyOtp ( {
101- email,
102- token : otp ,
103- type : 'recovery' ,
104- } ) ;
105-
106- if ( error ) throw error ;
107- setStep ( 3 ) ;
108- setMessage ( 'Code verified. Please enter your new password.' ) ;
101+ const { error } = await supabase . auth . verifyOtp ( {
102+ email,
103+ token : otp ,
104+ type : 'recovery' ,
105+ } ) ;
106+
107+ if ( error ) throw error ;
108+ setStep ( 3 ) ;
109+ setMessage ( 'Code verified. Please enter your new password.' ) ;
109110 } catch ( err ) {
110- console . error ( 'OTP verification error:' , err ) ;
111- setError ( 'Invalid or expired code. Please check your email and try again.' ) ;
111+ console . error ( 'OTP verification error:' , err ) ;
112+ setError ( 'Invalid or expired code. Please check your email and try again.' ) ;
112113 } finally {
113- setLoading ( false ) ;
114+ setLoading ( false ) ;
114115 }
115- } ;
116+ } ;
116117
117- const handleUpdatePassword = async ( e ) => {
118+ const handleUpdatePassword = async ( e ) => {
118119 e . preventDefault ( ) ;
119- if ( ! newPassword || newPassword . length < 6 ) {
120- setError ( 'Password must be at least 6 characters long.' ) ;
121- return ;
120+ const passwordError = validatePassword ( newPassword ) ;
121+ if ( passwordError ) {
122+ setError ( passwordError ) ;
123+ return ;
122124 }
123125
124126 setLoading ( true ) ;
@@ -129,9 +131,9 @@ function ForgotPassword() {
129131 { /* Background Patterns */ }
130132 < div className = "absolute top-0 left-0 w-full h-full opacity-[0.03] pointer-events-none" style = { { backgroundImage : `url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")` } } > </ div >
131133 < div className = "absolute top-0 left-0 w-[500px] h-[500px] bg-emerald-500/10 rounded-full blur-[120px] -translate-x-1/2 -translate-y-1/2 pointer-events-none" > </ div >
132-
134+
133135 < div className = "w-full max-w-md relative z-10 flex flex-col items-center" >
134-
136+
135137 { /* Back Button - Expose beautifully at the top */ }
136138 < Link
137139 to = "/login"
@@ -151,7 +153,7 @@ function ForgotPassword() {
151153 </ Link >
152154 </ div >
153155
154- < motion . div
156+ < motion . div
155157 initial = { { opacity : 0 , y : 20 } }
156158 animate = { { opacity : 1 , y : 0 } }
157159 className = "bg-white dark:bg-[#1a2e24] border border-white/50 dark:border-[#2a4034] shadow-[0_20px_50px_rgba(0,0,0,0.08)] dark:shadow-[0_20px_50px_rgba(0,0,0,0.4)] rounded-[2.5rem] p-6 sm:p-10 relative overflow-hidden w-full"
@@ -169,7 +171,7 @@ function ForgotPassword() {
169171
170172 < AnimatePresence mode = "wait" >
171173 { step === 4 ? (
172- < motion . div
174+ < motion . div
173175 key = "success"
174176 initial = { { opacity : 0 , scale : 0.95 } }
175177 animate = { { opacity : 1 , scale : 1 } }
@@ -192,7 +194,7 @@ function ForgotPassword() {
192194 ) : (
193195 < div className = "space-y-6" >
194196 { error && (
195- < motion . div
197+ < motion . div
196198 initial = { { opacity : 0 , height : 0 } }
197199 animate = { { opacity : 1 , height : 'auto' } }
198200 className = "bg-red-50 dark:bg-red-950/20 border border-red-100 dark:border-red-900/30 text-red-600 dark:text-red-400 px-5 py-4 rounded-2xl text-sm font-semibold flex items-start gap-3"
@@ -258,9 +260,8 @@ function ForgotPassword() {
258260 autoFocus
259261 />
260262 </ div >
261- < p className = { `text-center text-xs font-bold mt-1 ${
262- timerExpired ? 'text-red-500' : timeLeft < 60 ? 'text-orange-500' : 'text-slate-400'
263- } `} >
263+ < p className = { `text-center text-xs font-bold mt-1 ${ timerExpired ? 'text-red-500' : timeLeft < 60 ? 'text-orange-500' : 'text-slate-400'
264+ } `} >
264265 { timerExpired ? '⚠ Code expired — request a new one below' : `Expires in ${ formatTime ( timeLeft ) } ` }
265266 </ p >
266267 </ div >
@@ -310,7 +311,7 @@ function ForgotPassword() {
310311
311312 < button
312313 type = "submit"
313- disabled = { loading || newPassword . length < 6 }
314+ disabled = { loading || newPassword . length < 8 }
314315 className = "w-full rounded-2xl py-4 font-bold transition-all flex items-center justify-center gap-2 text-white bg-gradient-to-r from-emerald-600 to-emerald-500 hover:from-emerald-700 hover:to-emerald-600 shadow-lg"
315316 onMouseEnter = { ( e ) => e . currentTarget . style . transform = 'translateY(-2px)' }
316317 onMouseLeave = { ( e ) => e . currentTarget . style . transform = 'translateY(0)' }
0 commit comments