@@ -17,7 +17,15 @@ import { useAuthStore } from '@/state/useAuthStore';
1717import { axiosClient } from '@/network/axiosClient' ;
1818import { logout } from '@/lib/auth' ;
1919import { useRouter } from 'next/navigation' ;
20+ import { validateEmail , validatePassword , validateUsername } from '@/lib/utils' ;
21+ import { ValidationError } from '@/types/validation' ;
2022
23+ /**
24+ * ValidationError: For the form validation errors
25+ * ProfileMessage: For the profile related API response message
26+ * PasswordMessage: For the password related API response message
27+ * @returns profile component
28+ */
2129const ProfilePage = ( ) => {
2230 const { user, setUser, clearAuth } = useAuthStore ( ) ;
2331 const router = useRouter ( ) ;
@@ -61,19 +69,62 @@ const ProfilePage = () => {
6169 confirmPassword : false ,
6270 } ) ;
6371
72+ const [ validationErrors , setValidationErrors ] = useState < ValidationError > ( { } ) ;
73+
6474 // Add function to check if form data has changed
6575 const hasProfileChanges = ( ) => {
6676 return (
6777 formData . username !== user ?. username || formData . email !== user ?. email
6878 ) ;
6979 } ;
7080
81+ const validateProfileForm = ( ) : boolean => {
82+ const errors : ValidationError = { } ;
83+ let isValid = true ;
84+
85+ // Validate email
86+ const emailErrors = validateEmail ( formData . email ) ;
87+ if ( emailErrors . length > 0 ) {
88+ errors . email = emailErrors ;
89+ isValid = false ;
90+ }
91+
92+ // Validate username
93+ const usernameErrors = validateUsername ( formData . username ) ;
94+ if ( usernameErrors . length > 0 ) {
95+ errors . username = [ 'Username is required' ] ;
96+ isValid = false ;
97+ }
98+
99+ setValidationErrors ( errors ) ;
100+ return isValid ;
101+ } ;
102+
103+ const validatePasswordForm = ( ) : boolean => {
104+ const errors : ValidationError = { } ;
105+ let isValid = true ;
106+
107+ // Validate new password
108+ const passwordErrors = validatePassword (
109+ passwordData . newPassword ,
110+ passwordData . confirmPassword ,
111+ ) ;
112+ if ( passwordErrors . length > 0 ) {
113+ errors . newPassword = passwordErrors ;
114+ isValid = false ;
115+ }
116+
117+ setValidationErrors ( errors ) ;
118+ return isValid ;
119+ } ;
120+
71121 // Add form handlers
72122 const handleProfileSubmit = async ( e : FormEvent < HTMLFormElement > ) => {
73123 e . preventDefault ( ) ;
74124 setProfileMessage ( null ) ;
125+ setValidationErrors ( { } ) ;
75126
76- if ( ! user ) return ;
127+ if ( ! user || ! validateProfileForm ( ) ) return ;
77128
78129 try {
79130 const result = await axiosClient . patch ( `/users/${ user . id } ` , {
@@ -108,16 +159,9 @@ const ProfilePage = () => {
108159 const handlePasswordSubmit = async ( e : FormEvent ) => {
109160 e . preventDefault ( ) ;
110161 setPasswordMessage ( null ) ;
162+ setValidationErrors ( { } ) ;
111163
112- if ( ! user ) return ;
113-
114- if ( passwordData . newPassword !== passwordData . confirmPassword ) {
115- setPasswordMessage ( {
116- type : 'error' ,
117- text : 'New password and confirm password do not match' ,
118- } ) ;
119- return ;
120- }
164+ if ( ! user || ! validatePasswordForm ( ) ) return ;
121165
122166 try {
123167 const verifyResult = await axiosClient . post (
@@ -135,25 +179,21 @@ const ProfilePage = () => {
135179 password : passwordData . newPassword ,
136180 } ) ;
137181
138- // throw error if result is not 200
139182 if ( result . status !== 200 ) {
140183 throw new Error ( result . data . message ) ;
141184 }
142185
143- // Sucess state
144186 setPasswordMessage ( {
145187 type : 'success' ,
146188 text : 'Password updated successfully' ,
147189 } ) ;
148190
149- // reset the fields
150191 setPasswordData ( {
151192 currentPassword : '' ,
152193 newPassword : '' ,
153194 confirmPassword : '' ,
154195 } ) ;
155196
156- // Clear message after 5 seconds
157197 setTimeout ( ( ) => {
158198 setPasswordMessage ( null ) ;
159199 } , 5000 ) ;
@@ -168,13 +208,15 @@ const ProfilePage = () => {
168208 const handleDeleteAccount = async ( ) => {
169209 const res = await axiosClient . delete ( `/users/${ user ?. id } ` ) ;
170210 if ( res . status === 200 ) {
171- const res = await logout ( ) ;
211+ const resLogout = await logout ( ) ;
172212
173- if ( res ) {
174- clearAuth ( ) ;
175- router . push ( '/' ) ;
213+ if ( ! resLogout ) {
176214 return ;
177215 }
216+
217+ clearAuth ( ) ;
218+ router . push ( '/' ) ;
219+ return ;
178220 }
179221 } ;
180222
@@ -198,12 +240,23 @@ const ProfilePage = () => {
198240 < Input
199241 id = "username"
200242 placeholder = "Enter username"
201- className = "border-slate-700 bg-slate-900 text-slate-200"
243+ className = { `border-slate-700 bg-slate-900 text-slate-200 ${
244+ validationErrors . username ? 'border-red-500' : ''
245+ } `}
202246 value = { formData . username }
203247 onChange = { ( e ) =>
204248 setFormData ( { ...formData , username : e . target . value } )
205249 }
206250 />
251+ { validationErrors . username && (
252+ < div className = "mt-1 space-y-1" >
253+ { validationErrors . username . map ( ( error , index ) => (
254+ < p key = { index } className = "text-xs text-red-500" >
255+ { error }
256+ </ p >
257+ ) ) }
258+ </ div >
259+ ) }
207260 </ div >
208261 < div className = "space-y-2" >
209262 < Label htmlFor = "email" className = "text-slate-200" >
@@ -213,12 +266,23 @@ const ProfilePage = () => {
213266 id = "email"
214267 type = "email"
215268 placeholder = "Enter email"
216- className = "border-slate-700 bg-slate-900 text-slate-200"
269+ className = { `border-slate-700 bg-slate-900 text-slate-200 ${
270+ validationErrors . email ? 'border-red-500' : ''
271+ } `}
217272 value = { formData . email }
218273 onChange = { ( e ) =>
219274 setFormData ( { ...formData , email : e . target . value } )
220275 }
221276 />
277+ { validationErrors . email && (
278+ < div className = "mt-1 space-y-1" >
279+ { validationErrors . email . map ( ( error , index ) => (
280+ < p key = { index } className = "text-xs text-red-500" >
281+ { error }
282+ </ p >
283+ ) ) }
284+ </ div >
285+ ) }
222286 </ div >
223287 < Button
224288 type = "submit"
@@ -279,6 +343,7 @@ const ProfilePage = () => {
279343 </ button >
280344 </ div >
281345 </ div >
346+
282347 < div className = "space-y-2" >
283348 < Label htmlFor = "new-password" className = "text-slate-200" >
284349 New Password
@@ -287,7 +352,9 @@ const ProfilePage = () => {
287352 < Input
288353 id = "new-password"
289354 type = { showPasswords . newPassword ? 'text' : 'password' }
290- className = "border-slate-700 bg-slate-900 text-slate-200"
355+ className = { `border-slate-700 bg-slate-900 text-slate-200 ${
356+ validationErrors . newPassword ? 'border-red-500' : ''
357+ } `}
291358 value = { passwordData . newPassword }
292359 onChange = { ( e ) =>
293360 setPasswordData ( {
@@ -313,7 +380,17 @@ const ProfilePage = () => {
313380 ) }
314381 </ button >
315382 </ div >
383+ { validationErrors . newPassword && (
384+ < div className = "mt-1 space-y-1" >
385+ { validationErrors . newPassword . map ( ( error , index ) => (
386+ < p key = { index } className = "text-xs text-red-500" >
387+ { error }
388+ </ p >
389+ ) ) }
390+ </ div >
391+ ) }
316392 </ div >
393+
317394 < div className = "space-y-2" >
318395 < Label htmlFor = "confirm-password" className = "text-slate-200" >
319396 Confirm New Password
@@ -322,7 +399,7 @@ const ProfilePage = () => {
322399 < Input
323400 id = "confirm-password"
324401 type = { showPasswords . confirmPassword ? 'text' : 'password' }
325- className = " border-slate-700 bg-slate-900 text-slate-200"
402+ className = { ` border-slate-700 bg-slate-900 text-slate-200` }
326403 value = { passwordData . confirmPassword }
327404 onChange = { ( e ) =>
328405 setPasswordData ( {
@@ -349,6 +426,7 @@ const ProfilePage = () => {
349426 </ button >
350427 </ div >
351428 </ div >
429+
352430 < Button
353431 type = "submit"
354432 className = "bg-[#4ADE80] text-slate-900 hover:bg-[#4ADE80]/90"
@@ -358,7 +436,11 @@ const ProfilePage = () => {
358436 </ form >
359437 { passwordMessage && (
360438 < div
361- className = { `mt-2 text-sm ${ passwordMessage . type === 'error' ? 'text-red-500' : 'text-green-500' } ` }
439+ className = { `mt-2 text-sm ${
440+ passwordMessage . type === 'error'
441+ ? 'text-red-500'
442+ : 'text-green-500'
443+ } `}
362444 >
363445 { passwordMessage . text }
364446 </ div >
0 commit comments