1+ 'use client' 
2+ 
3+ import  {  useEffect ,  useState  }  from  'react' 
4+ import  {  useSession ,  getSession  }  from  'next-auth/react' 
5+ import  {  useRouter  }  from  'next/navigation' 
6+ import  Link  from  'next/link' 
7+ import  Image  from  'next/image' 
8+ import  {  
9+   FaUser ,  
10+   FaGithub ,  
11+   FaGoogle ,  
12+   FaLock , 
13+   FaCog 
14+ }  from  'react-icons/fa' 
15+ 
16+ interface  OAuthAccount  { 
17+   id : number 
18+   provider : string 
19+   provider_user_id : string 
20+   created_at : string 
21+ } 
22+ 
23+ export  default  function  AccountPage ( )  { 
24+   const  {  data : session ,  status }  =  useSession ( ) 
25+   const  router  =  useRouter ( ) 
26+   const  [ activeTab ,  setActiveTab ]  =  useState ( 'general' ) 
27+   const  [ oauthAccounts ,  setOAuthAccounts ]  =  useState < OAuthAccount [ ] > ( [ ] ) 
28+   const  [ loading ,  setLoading ]  =  useState ( true ) 
29+ 
30+   useEffect ( ( )  =>  { 
31+     if  ( status  ===  'unauthenticated' )  { 
32+       router . push ( '/login' ) 
33+       return 
34+     } 
35+ 
36+     if  ( status  ===  'authenticated' )  { 
37+       fetchOAuthAccounts ( ) 
38+     } 
39+   } ,  [ status ,  router ] ) 
40+ 
41+   const  fetchOAuthAccounts  =  async  ( )  =>  { 
42+     try  { 
43+       const  response  =  await  fetch ( '/api/user/oauth-accounts' ) 
44+       if  ( response . ok )  { 
45+         const  accounts  =  await  response . json ( ) 
46+         setOAuthAccounts ( accounts ) 
47+       } 
48+     }  catch  ( error )  { 
49+       console . error ( 'Failed to fetch OAuth accounts:' ,  error ) 
50+     }  finally  { 
51+       setLoading ( false ) 
52+     } 
53+   } 
54+ 
55+   if  ( status  ===  'loading'  ||  loading )  { 
56+     return  ( 
57+       < div  className = "min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center" > 
58+         < div  className = "text-center" > 
59+           < div  className = "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto" > </ div > 
60+           < p  className = "mt-2 text-gray-600 dark:text-gray-400" > Loading...</ p > 
61+         </ div > 
62+       </ div > 
63+     ) 
64+   } 
65+ 
66+   if  ( ! session ?. user )  { 
67+     return  null 
68+   } 
69+ 
70+   const  getProviderIcon  =  ( provider : string )  =>  { 
71+     switch  ( provider )  { 
72+       case  'github' :
73+         return  < FaGithub  className = "w-5 h-5"  /> 
74+       case  'google' :
75+         return  < FaGoogle  className = "w-5 h-5"  /> 
76+       default :
77+         return  < FaLock  className = "w-5 h-5"  /> 
78+     } 
79+   } 
80+ 
81+   const  getProviderName  =  ( provider : string )  =>  { 
82+     switch  ( provider )  { 
83+       case  'github' :
84+         return  'GitHub' 
85+       case  'google' :
86+         return  'Google' 
87+       default :
88+         return  provider . charAt ( 0 ) . toUpperCase ( )  +  provider . slice ( 1 ) 
89+     } 
90+   } 
91+ 
92+   const  formatDate  =  ( dateString : string )  =>  { 
93+     return  new  Date ( dateString ) . toLocaleDateString ( 'en-US' ,  { 
94+       year : 'numeric' , 
95+       month : 'long' , 
96+       day : 'numeric' 
97+     } ) 
98+   } 
99+ 
100+   const  sidebarTabs  =  [ 
101+     {  id : 'general' ,  name : 'General' ,  icon : FaUser  } , 
102+     {  id : 'security' ,  name : 'Security' ,  icon : FaLock ,  disabled : true  } , 
103+     {  id : 'preferences' ,  name : 'Preferences' ,  icon : FaCog ,  disabled : true  } , 
104+   ] 
105+ 
106+   return  ( 
107+     < div  className = "min-h-screen bg-gray-50 dark:bg-gray-900" > 
108+       { /* Header */ } 
109+       < div  className = "bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700" > 
110+         < div  className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4" > 
111+           < div  className = "flex items-center space-x-4" > 
112+             < Link  
113+               href = "/" 
114+               className = "text-blue-600 hover:text-blue-700 text-sm font-medium" 
115+             > 
116+               ← Back to Home
117+             </ Link > 
118+             < h1  className = "text-2xl font-bold text-gray-900 dark:text-white" > 
119+               Account Settings
120+             </ h1 > 
121+           </ div > 
122+         </ div > 
123+       </ div > 
124+ 
125+       < div  className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6" > 
126+         < div  className = "flex flex-col lg:flex-row gap-6" > 
127+           { /* Sidebar */ } 
128+           < div  className = "w-full lg:w-64" > 
129+             < nav  className = "bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-1" > 
130+               { sidebarTabs . map ( ( tab )  =>  ( 
131+                 < button 
132+                   key = { tab . id } 
133+                   onClick = { ( )  =>  ! tab . disabled  &&  setActiveTab ( tab . id ) } 
134+                   disabled = { tab . disabled } 
135+                   className = { `w-full flex items-center px-3 py-2 text-sm font-medium rounded-md transition-colors ${  
136+                     activeTab  ===  tab . id  
137+                       ? 'bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300'  
138+                       : tab . disabled  
139+                       ? 'text-gray-400 dark:text-gray-600 cursor-not-allowed'  
140+                       : 'text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'  
141+                   }  `} 
142+                 > 
143+                   < tab . icon  className = "w-4 h-4 mr-3"  /> 
144+                   { tab . name } 
145+                   { tab . disabled  &&  ( 
146+                     < span  className = "ml-auto text-xs text-gray-400" > Soon</ span > 
147+                   ) } 
148+                 </ button > 
149+               ) ) } 
150+             </ nav > 
151+           </ div > 
152+ 
153+           { /* Content */ } 
154+           < div  className = "flex-1" > 
155+             < div  className = "bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700" > 
156+               { activeTab  ===  'general'  &&  ( 
157+                 < div  className = "p-6" > 
158+                   < h2  className = "text-lg font-semibold text-gray-900 dark:text-white mb-6" > 
159+                     General Information
160+                   </ h2 > 
161+ 
162+                   { /* User Profile */ } 
163+                   < div  className = "mb-8" > 
164+                     < h3  className = "text-md font-medium text-gray-900 dark:text-white mb-4" > 
165+                       Profile
166+                     </ h3 > 
167+                     < div  className = "flex items-start space-x-4" > 
168+                       { session . user . image  ? ( 
169+                         < Image 
170+                           src = { session . user . image } 
171+                           alt = { session . user . name  ||  'User' } 
172+                           width = { 64 } 
173+                           height = { 64 } 
174+                           className = "rounded-full" 
175+                         /> 
176+                       )  : ( 
177+                         < div  className = "w-16 h-16 bg-blue-600 rounded-full flex items-center justify-center text-white" > 
178+                           < FaUser  className = "w-6 h-6"  /> 
179+                         </ div > 
180+                       ) } 
181+                       < div  className = "flex-1" > 
182+                         < div  className = "grid grid-cols-1 md:grid-cols-2 gap-4" > 
183+                           < div > 
184+                             < label  className = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" > 
185+                               Name
186+                             </ label > 
187+                             < div  className = "px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md text-gray-900 dark:text-white" > 
188+                               { session . user . name  ||  'Not provided' } 
189+                             </ div > 
190+                           </ div > 
191+                           < div > 
192+                             < label  className = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" > 
193+                               Email
194+                             </ label > 
195+                             < div  className = "px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md text-gray-900 dark:text-white" > 
196+                               { session . user . email } 
197+                             </ div > 
198+                           </ div > 
199+                         </ div > 
200+                       </ div > 
201+                     </ div > 
202+                   </ div > 
203+ 
204+                   { /* Connected Accounts */ } 
205+                   < div > 
206+                     < h3  className = "text-md font-medium text-gray-900 dark:text-white mb-4" > 
207+                       Connected Accounts
208+                     </ h3 > 
209+                     < div  className = "space-y-3" > 
210+                       { oauthAccounts . map ( ( account )  =>  ( 
211+                         < div 
212+                           key = { account . id } 
213+                           className = "flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600" 
214+                         > 
215+                           < div  className = "flex items-center space-x-3" > 
216+                             { getProviderIcon ( account . provider ) } 
217+                             < div > 
218+                               < p  className = "font-medium text-gray-900 dark:text-white" > 
219+                                 { getProviderName ( account . provider ) } 
220+                               </ p > 
221+                               < p  className = "text-sm text-gray-500 dark:text-gray-400" > 
222+                                 Connected on { formatDate ( account . created_at ) } 
223+                               </ p > 
224+                             </ div > 
225+                           </ div > 
226+                           < div  className = "flex items-center space-x-2" > 
227+                             < span  className = "inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-300" > 
228+                               Connected
229+                             </ span > 
230+                           </ div > 
231+                         </ div > 
232+                       ) ) } 
233+                       
234+                       { oauthAccounts . length  ===  0  &&  ( 
235+                         < p  className = "text-gray-500 dark:text-gray-400 text-sm" > 
236+                           No connected accounts found.
237+                         </ p > 
238+                       ) } 
239+                     </ div > 
240+                   </ div > 
241+                 </ div > 
242+               ) } 
243+             </ div > 
244+           </ div > 
245+         </ div > 
246+       </ div > 
247+     </ div > 
248+   ) 
249+ } 
0 commit comments