@@ -41,24 +41,24 @@ function ForgotPasswordModal({ isOpen, onClose }: Props) {
4141 const hasEmail = email . trim ( ) . length > 0 ;
4242 return (
4343 < div className = "fixed inset-0 flex items-center justify-center bg-black/50 z-50" >
44- < div className = "bg-[#FFFFFF] w-md h-[248px] rounded-2xl border-t border-t-[#0000001A] shadow-[0_10px_15px_-3px_rgba(0,0,0,0.1),0_4px_6px_-4px_rgba(0,0,0,0.1)] p-6 relative" >
44+ < div className = "bg-[#FFFFFF] w-[448px] h-[248px] rounded-2xl border-t border-t-[#0000001A] shadow-[0_10px_15px_-3px_rgba(0,0,0,0.1),0_4px_6px_-4px_rgba(0,0,0,0.1)] p-6 relative" >
4545 { /*Close button*/ }
4646 < button onClick = { onClose } className = "absolute top-4 right-4" >
4747 < X className = "w-4 h-4 text-[#555555] opacity-70" />
4848 </ button >
4949
5050 < div className = "absolute top-[25px] left-[25px] w-[398px] h-[66px]" >
5151 { /*Reset Password Heading*/ }
52- < h2 className = "font- poppins font-semibold text-[18px] leading-[18px] text-[#555555]" >
52+ < h2 className = { ` ${ poppins . className } font-semibold text-[18px] leading-[18px] text-[#555555]` } >
5353 Reset Password
5454 </ h2 >
5555
56- < p className = "font- manrope font-regular text-[14px] leading-5 text-[#717182] mt-2" >
57- Enter your email address and we' ll send you a link
56+ < p className = { ` ${ manrope . className } font-regular text-[14px] leading-5 text-[#717182] mt-2` } >
57+ Enter your email address and we' ll send you a link
5858 to reset your pass.
5959 </ p >
6060
61- < p className = "font- manrope font-medium text-[14px] leading-5 w-[92px] h-5 mt-4 text-[#555555]" >
61+ < p className = { ` ${ manrope . className } font-medium text-[14px] leading-5 w-[92px] h-5 mt-4 text-[#555555]` } >
6262 Email Address
6363 </ p >
6464
@@ -68,20 +68,19 @@ function ForgotPasswordModal({ isOpen, onClose }: Props) {
6868 value = { email }
6969 onChange = { ( e ) => setEmail ( e . target . value ) }
7070 className = "w-full rounded-xl border border-neutral-200 bg-neutral-100/70 px-4 py-3 text-neutral-800 outline-none ring-0 placeholder:text-neutral-400 focus:border-rose-300 focus:bg-white focus:shadow focus:shadow-rose-100"
71- > </ input >
71+ / >
7272
7373 < div className = "flex gap-3 mt-4 items-center" >
7474 < button
75- className = "w-[193px] h-9 rounded-[14px] border border-[#0000001A] py-2 px-4 font-manrope font-medium leading-5 hover:opacity-80
76- text-[#555555] font-manrope text-[14px]"
75+ className = "w-[193px] h-9 rounded-[14px] border border-[#0000001A] py-2 px-4 font-manrope font-medium leading-5 hover:opacity-80 text-[#555555] text-[14px]"
7776 onClick = { onClose }
7877 >
7978 Cancel
8079 </ button >
8180
8281 < button
8382 type = "button"
84- className = "w-[193px] h-9 rounded-[14px] bg-[#D26879] py-2 px-4 font-manrope font-medium leading-5 text-[#FFFFFF] font-manrope text-[14px]"
83+ className = "w-[193px] h-9 rounded-[14px] bg-[#D26879] py-2 px-4 font-manrope font-medium leading-5 text-[#FFFFFF] text-[14px]"
8584 style = { {
8685 backgroundColor : hasEmail
8786 ? "#E76C82"
@@ -107,14 +106,30 @@ export default function LogIn() {
107106 const [ isOpen , setIsOpen ] = useState ( false ) ;
108107 const [ email , setEmail ] = useState ( "" ) ;
109108 const [ password , setPassword ] = useState ( "" ) ;
110- const [ error , setError ] = useState < string | null > ( ) ;
109+ const [ error , setError ] = useState < string | null > ( null ) ;
110+ const [ errors , setErrors ] = useState < { email ?: string ; password ?: string } > ( { } ) ;
111111
112112 async function handleClick ( e : React . MouseEvent ) {
113113 e . preventDefault ( ) ;
114+
115+ // Client-side validation
116+ const newErrors : { email ?: string ; password ?: string } = { } ;
117+ if ( ! email . trim ( ) ) newErrors . email = "Email is required" ;
118+ if ( ! password . trim ( ) ) newErrors . password = "Password is required" ;
119+
120+ if ( Object . keys ( newErrors ) . length > 0 ) {
121+ setErrors ( newErrors ) ;
122+ return ;
123+ }
124+
125+ // Clear errors for API attempt
126+ setErrors ( { } ) ;
127+ setError ( null ) ;
128+
114129 try {
115130 const result = await authClient . signIn . email ( {
116- email : email ,
117- password : password ,
131+ email : email . trim ( ) ,
132+ password : password . trim ( ) ,
118133 } ) ;
119134
120135 if ( result . error ) {
@@ -189,7 +204,7 @@ export default function LogIn() {
189204 Welcome Back
190205 </ h1 >
191206 < p
192- className = { `${ manrope . className } mt-1 text-center text-sm text-neutral-500' ` }
207+ className = { `${ manrope . className } mt-1 text-center text-sm text-neutral-500` }
193208 style = { {
194209 color : "#4A5565" ,
195210 fontWeight : 400 ,
@@ -218,9 +233,18 @@ export default function LogIn() {
218233 name = "email"
219234 type = "email"
220235 placeholder = "you@example.com"
221- onChange = { ( e ) => setEmail ( e . target . value ) }
222- className = "w-full rounded-xl border border-neutral-200 bg-neutral-100/70 px-4 py-3 text-neutral-800 outline-none ring-0 placeholder:text-neutral-400 focus:border-rose-300 focus:bg-white focus:shadow focus:shadow-rose-100"
236+ value = { email }
237+ onChange = { ( e ) => {
238+ setEmail ( e . target . value ) ;
239+ if ( errors . email ) setErrors ( prev => ( { ...prev , email : undefined } ) ) ;
240+ } }
241+ className = { `w-full rounded-xl border ${ errors . email ? 'border-red-300 bg-red-50' : 'border-neutral-200 bg-neutral-100/70' } px-4 py-3 text-neutral-800 outline-none ring-0 placeholder:text-neutral-400 focus:border-rose-300 focus:bg-white focus:shadow focus:shadow-rose-100` }
223242 />
243+ { errors . email && (
244+ < p className = "mt-1 text-xs text-red-600 font-medium" role = "alert" >
245+ { errors . email }
246+ </ p >
247+ ) }
224248 </ div >
225249
226250 { /* PASSWORD FIELD */ }
@@ -236,16 +260,25 @@ export default function LogIn() {
236260 name = "password"
237261 type = "password"
238262 placeholder = "••••••••"
239- onChange = { ( e ) => setPassword ( e . target . value ) }
240- className = "w-full rounded-xl border border-neutral-200 bg-neutral-100/70 px-4 py-3 text-neutral-800 outline-none ring-0 placeholder:text-neutral-400 focus:border-rose-300 focus:bg-white focus:shadow focus:shadow-rose-100"
263+ value = { password }
264+ onChange = { ( e ) => {
265+ setPassword ( e . target . value ) ;
266+ if ( errors . password ) setErrors ( prev => ( { ...prev , password : undefined } ) ) ;
267+ } }
268+ className = { `w-full rounded-xl border ${ errors . password ? 'border-red-300 bg-red-50' : 'border-neutral-200 bg-neutral-100/70' } px-4 py-3 text-neutral-800 outline-none ring-0 placeholder:text-neutral-400 focus:border-rose-300 focus:bg-white focus:shadow focus:shadow-rose-100` }
241269 />
270+ { errors . password && (
271+ < p className = "mt-1 text-xs text-red-600 font-medium" role = "alert" >
272+ { errors . password }
273+ </ p >
274+ ) }
242275 </ div >
243276
244277 { /* PRIMARY BUTTON */ }
245278 < button
246279 type = "submit"
247280 onClick = { handleClick }
248- className = { `${ manrope . className } mt-1 w-full text-center text-white transition ` }
281+ className = { `${ manrope . className } mt-1 w-full text-center text-white` }
249282 style = { {
250283 backgroundColor : "#E59AA8" ,
251284 width : "318px" ,
@@ -270,7 +303,7 @@ export default function LogIn() {
270303 < div className = "text-center" >
271304 < button
272305 type = "button"
273- className = { `${ manrope . className } underline-offset-4 hover:underline ` }
306+ className = { `${ manrope . className } ` }
274307 onClick = { ( ) => setIsOpen ( true ) }
275308 style = { {
276309 color : "#E76C82" ,
@@ -297,7 +330,6 @@ export default function LogIn() {
297330 </ section >
298331
299332 { /* Modal shell included but hidden */ }
300-
301333 < ForgotPasswordModal
302334 isOpen = { isOpen }
303335 onClose = { ( ) => setIsOpen ( false ) }
0 commit comments