@@ -2,19 +2,20 @@ import { useEffect, useState } from 'react';
22import { Pencil , Save } from "lucide-react" ;
33import { useAppDispatch , useAppSelector } from '../hooks' ;
44import { setAuth } from '../slices/authSlice' ;
5- import type { User } from '../types' ;
6- import { ConfirmMenu , Preference } from '../components' ;
5+ import type { User , UserWithoutEmail } from '../types' ;
6+ import { ConfirmMenu , Loader , Preference } from '../components' ;
77import { useConfirmExit } from '../hooks/useConfirmExit' ;
88
99export default function SettingsPage ( ) {
1010 const user = useAppSelector ( state => state . auth . user ) ;
1111 const [ isEditingName , setIsEditingName ] = useState ( false ) ;
1212 const dispatch = useAppDispatch ( ) ;
1313 const [ confirmUpdate , setConfirmUpdate ] = useState ( false ) ;
14- const [ initialDetails , setInitialDetails ] = useState < User | null > ( null ) ;
15- const [ details , setDetails ] = useState < User > ( {
14+ const [ initialDetails , setInitialDetails ] = useState < UserWithoutEmail | null > ( null ) ;
15+ const [ isLoading , setIsLoading ] = useState ( false ) ;
16+
17+ const [ details , setDetails ] = useState < UserWithoutEmail > ( {
1618 name : '' ,
17- email : '' ,
1819 timezone : 'UTC' ,
1920 emailNotifications : true ,
2021 pushAlerts : true ,
@@ -23,25 +24,25 @@ export default function SettingsPage() {
2324 } ) ;
2425
2526 useEffect ( ( ) => {
26- if ( ! user )
27- return
27+ if ( ! user ) return ;
2828 const value = {
2929 name : user . name ,
30- email : user . email ,
3130 timezone : user . timezone ,
3231 emailNotifications : user . emailNotifications ,
3332 pushAlerts : user . pushAlerts ,
3433 mode : user . mode ,
3534 timeFormat24 : user . timeFormat24
36- }
35+ } ;
3736 setDetails ( value ) ;
3837 setInitialDetails ( value ) ;
3938 } , [ user ] ) ;
4039
4140 const isDirty = JSON . stringify ( details ) !== JSON . stringify ( initialDetails ) ;
42- useConfirmExit ( isDirty ) ;
41+ useConfirmExit ( isDirty , ! isLoading ) ;
4342
4443 const handleSaveChanges = ( ) => {
44+ setIsLoading ( true ) ;
45+ if ( ! user ) return ;
4546
4647 fetch ( `${ import . meta. env . VITE_BACKEND_URL } /` , {
4748 method : "PUT" ,
@@ -61,6 +62,8 @@ export default function SettingsPage() {
6162 . then ( data => {
6263 const userDetails : User = data . user ;
6364 setDetails ( userDetails ) ;
65+ setInitialDetails ( userDetails ) ;
66+ setIsEditingName ( false ) ;
6467 dispatch ( setAuth ( {
6568 user : {
6669 name : userDetails . name ,
@@ -71,16 +74,18 @@ export default function SettingsPage() {
7174 emailNotifications : userDetails . emailNotifications ,
7275 pushAlerts : userDetails . pushAlerts
7376 }
74- } ) )
77+ } ) ) ;
7578 } )
7679 . catch ( err => console . error ( err ) )
80+ . finally ( ( ) => setIsLoading ( false ) ) ;
7781 }
7882
7983 if ( ! user ) return < div > Loading user info...</ div > ;
8084
8185
8286 return (
8387 < >
88+ { isLoading && < Loader /> }
8489 < h1 className = "text-3xl text-purple-600 mb-6" > Settings</ h1 >
8590
8691 < form onSubmit = { e => {
@@ -89,43 +94,43 @@ export default function SettingsPage() {
8994 } } className = "space-y-10 bg-white p-6 rounded-xl shadow" >
9095
9196 < div className = 'border border-gray-200 rounded-lg px-4 py-6 space-y-6' >
92- < h2 className = "text-lg font-semibold text-gray-800 mb-4" > Profile</ h2 >
93-
94- < div className = "space-y-4" >
95- < div >
96- < label className = "block mb-1 font-medium text-gray-700" > Full Name</ label >
97- < div className = "flex items-center gap-2" >
98- < input
99- type = "text"
100- value = { details . name }
101- onChange = { e => setDetails ( { ...details , name : e . target . value } ) }
102- readOnly = { ! isEditingName }
103- className = { `flex-1 rounded-md px-3 py-2 border transition
104- ${ isEditingName
105- ? 'bg-white border-gray-300 focus:outline-none focus:ring-2 focus:ring-purple-500'
106- : 'bg-gray-100 text-gray-500 cursor-not-allowed border-gray-200'
107- } `}
108- />
109- < button
110- type = "button"
111- onClick = { ( ) => setIsEditingName ( ! isEditingName ) }
112- className = "px-3 py-2.5 bg-gray-100 border border-gray-300 rounded hover:text-purple-600 transition"
113- title = { isEditingName ? "Lock" : "Edit Name" }
114- >
115- { isEditingName ? < Save className = "w-4 h-4" /> : < Pencil className = "w-4 h-4" /> }
116- </ button >
117- </ div >
118- </ div >
119- < div >
120- < label className = "block mb-1 font-medium text-gray-700" > Email</ label >
97+ < h2 className = "text-lg font-semibold text-gray-800 mb-4" > Profile</ h2 >
98+
99+ < div className = "space-y-4" >
100+ < div >
101+ < label className = "block mb-1 font-medium text-gray-700" > Full Name</ label >
102+ < div className = "flex items-center gap-2" >
121103 < input
122- type = "email"
123- value = { details . email }
124- readOnly
125- className = "w-full mt-1 bg-gray-100 border border-gray-300 rounded px-3 py-2 text-gray-500 cursor-not-allowed"
104+ type = "text"
105+ value = { details . name }
106+ onChange = { e => setDetails ( { ...details , name : e . target . value } ) }
107+ readOnly = { ! isEditingName }
108+ className = { `flex-1 rounded-md px-3 py-2 border transition
109+ ${ isEditingName
110+ ? 'bg-white border-gray-300 focus:outline-none focus:ring-2 focus:ring-purple-500'
111+ : 'bg-gray-100 text-gray-500 cursor-not-allowed border-gray-200'
112+ } `}
126113 />
127- </ div >
114+ < button
115+ type = "button"
116+ onClick = { ( ) => setIsEditingName ( ! isEditingName ) }
117+ className = "px-3 py-2.5 bg-gray-100 border border-gray-300 rounded hover:text-purple-600 transition"
118+ title = { isEditingName ? "Lock" : "Edit Name" }
119+ >
120+ { isEditingName ? < Save className = "w-4 h-4" /> : < Pencil className = "w-4 h-4" /> }
121+ </ button >
122+ </ div >
128123 </ div >
124+ < div >
125+ < label className = "block mb-1 font-medium text-gray-700" > Email</ label >
126+ < input
127+ type = "email"
128+ value = { user . email }
129+ readOnly
130+ className = "w-full mt-1 bg-gray-100 border border-gray-300 rounded px-3 py-2 text-gray-500 cursor-not-allowed"
131+ />
132+ </ div >
133+ </ div >
129134
130135 </ div >
131136
0 commit comments