1
+ "use client" ;
2
+
3
+ import { useState , useEffect } from "react" ;
4
+ import { auth , db } from "@/firebase/config" ;
5
+ import { updatePassword , deleteUser , reauthenticateWithCredential , EmailAuthProvider } from "firebase/auth" ;
6
+ import { doc , getDoc , deleteDoc } from "firebase/firestore" ;
7
+ import {
8
+ FaKey ,
9
+ FaUserCog ,
10
+ FaTrash ,
11
+ FaCopy ,
12
+ FaEye ,
13
+ FaEyeSlash ,
14
+ FaExclamationTriangle
15
+ } from "react-icons/fa" ;
16
+
17
+ export default function AccountPage ( ) {
18
+ const [ currentPassword , setCurrentPassword ] = useState ( "" ) ;
19
+ const [ newPassword , setNewPassword ] = useState ( "" ) ;
20
+ const [ confirmPassword , setConfirmPassword ] = useState ( "" ) ;
21
+ const [ showCurrentPassword , setShowCurrentPassword ] = useState ( false ) ;
22
+ const [ showNewPassword , setShowNewPassword ] = useState ( false ) ;
23
+ const [ showConfirmPassword , setShowConfirmPassword ] = useState ( false ) ;
24
+ const [ publicKey , setPublicKey ] = useState ( "" ) ;
25
+ const [ privateKey , setPrivateKey ] = useState ( "" ) ;
26
+ const [ loading , setLoading ] = useState ( false ) ;
27
+ const [ message , setMessage ] = useState ( { text : "" , type : "" } ) ;
28
+ const [ showDeleteConfirm , setShowDeleteConfirm ] = useState ( false ) ;
29
+
30
+ useEffect ( ( ) => {
31
+ const fetchKeys = async ( ) => {
32
+ if ( ! auth . currentUser ) return ;
33
+
34
+ try {
35
+ const userDoc = await getDoc ( doc ( db , "users" , auth . currentUser . uid ) ) ;
36
+ if ( userDoc . exists ( ) ) {
37
+ const data = userDoc . data ( ) ;
38
+ setPublicKey ( data . publicKey || "" ) ;
39
+ setPrivateKey ( data . privateKey || "" ) ;
40
+ }
41
+ } catch ( error ) {
42
+ console . error ( "Error fetching keys:" , error ) ;
43
+ setMessage ( { text : "Failed to load keys" , type : "error" } ) ;
44
+ }
45
+ } ;
46
+
47
+ fetchKeys ( ) ;
48
+ } , [ ] ) ;
49
+
50
+ const handleCopy = async ( text , label ) => {
51
+ try {
52
+ await navigator . clipboard . writeText ( text ) ;
53
+ setMessage ( { text : `${ label } copied to clipboard!` , type : "success" } ) ;
54
+ setTimeout ( ( ) => setMessage ( { text : "" , type : "" } ) , 3000 ) ;
55
+ } catch ( err ) {
56
+ setMessage ( { text : "Failed to copy to clipboard" , type : "error" } ) ;
57
+ }
58
+ } ;
59
+
60
+ const handlePasswordChange = async ( e ) => {
61
+ e . preventDefault ( ) ;
62
+ if ( newPassword !== confirmPassword ) {
63
+ setMessage ( { text : "New passwords don't match" , type : "error" } ) ;
64
+ return ;
65
+ }
66
+
67
+ setLoading ( true ) ;
68
+ try {
69
+ const user = auth . currentUser ;
70
+ const credential = EmailAuthProvider . credential (
71
+ user . email ,
72
+ currentPassword
73
+ ) ;
74
+ await reauthenticateWithCredential ( user , credential ) ;
75
+ await updatePassword ( user , newPassword ) ;
76
+
77
+ setMessage ( { text : "Password updated successfully!" , type : "success" } ) ;
78
+ setCurrentPassword ( "" ) ;
79
+ setNewPassword ( "" ) ;
80
+ setConfirmPassword ( "" ) ;
81
+ } catch ( error ) {
82
+ console . error ( "Error updating password:" , error ) ;
83
+ setMessage ( { text : "Failed to update password: " + error . message , type : "error" } ) ;
84
+ } finally {
85
+ setLoading ( false ) ;
86
+ }
87
+ } ;
88
+
89
+ const handleDeleteAccount = async ( ) => {
90
+ if ( ! currentPassword ) {
91
+ setMessage ( { text : "Please enter your current password" , type : "error" } ) ;
92
+ return ;
93
+ }
94
+
95
+ setLoading ( true ) ;
96
+ try {
97
+ const user = auth . currentUser ;
98
+ const credential = EmailAuthProvider . credential (
99
+ user . email ,
100
+ currentPassword
101
+ ) ;
102
+ await reauthenticateWithCredential ( user , credential ) ;
103
+
104
+ // Delete user data from Firestore
105
+ await deleteDoc ( doc ( db , "users" , user . uid ) ) ;
106
+
107
+ // Delete user account
108
+ await deleteUser ( user ) ;
109
+
110
+ // Redirect to home page
111
+ window . location . href = "/" ;
112
+ } catch ( error ) {
113
+ console . error ( "Error deleting account:" , error ) ;
114
+ setMessage ( { text : "Failed to delete account: " + error . message , type : "error" } ) ;
115
+ } finally {
116
+ setLoading ( false ) ;
117
+ }
118
+ } ;
119
+
120
+ return (
121
+ < div className = "flex items-center justify-center py-12 px-6" >
122
+ < div className = "bg-[#111827] rounded-2xl shadow-xl p-10 max-w-xl w-full" >
123
+ < h1 className = "text-3xl font-bold mb-6 text-center text-white flex items-center justify-center" >
124
+ Account Settings < FaUserCog className = "ml-3 w-8 h-8" />
125
+ </ h1 >
126
+
127
+ { message . text && (
128
+ < div
129
+ className = { `mb-4 p-3 rounded ${
130
+ message . type === "error"
131
+ ? "bg-red-500/10 text-red-500"
132
+ : "bg-green-500/10 text-green-500"
133
+ } `}
134
+ >
135
+ { message . text }
136
+ </ div >
137
+ ) }
138
+
139
+ { /* Keys Section */ }
140
+ < div className = "mb-8" >
141
+ < h2 className = "text-xl font-semibold mb-4 text-white flex items-center" >
142
+ < FaKey className = "w-5 h-5 mr-2" />
143
+ Your Keys
144
+ </ h2 >
145
+
146
+ < div className = "space-y-4" >
147
+ < div >
148
+ < label className = "block text-sm font-medium text-gray-300 mb-2" > Public Key</ label >
149
+ < div className = "relative" >
150
+ < textarea
151
+ readOnly
152
+ value = { publicKey }
153
+ className = "w-full p-2 bg-gray-900 border border-gray-700 rounded text-white text-sm font-mono"
154
+ rows = { 3 }
155
+ />
156
+ { publicKey && (
157
+ < button
158
+ onClick = { ( ) => handleCopy ( publicKey , "Public key" ) }
159
+ className = "absolute top-2 right-2 text-gray-400 hover:text-white"
160
+ >
161
+ < FaCopy />
162
+ </ button >
163
+ ) }
164
+ </ div >
165
+ </ div >
166
+
167
+ < div >
168
+ < label className = "block text-sm font-medium text-gray-300 mb-2" > Private Key</ label >
169
+ < div className = "relative" >
170
+ < textarea
171
+ readOnly
172
+ value = { privateKey }
173
+ className = "w-full p-2 bg-gray-900 border border-gray-700 rounded text-white text-sm font-mono"
174
+ rows = { 3 }
175
+ />
176
+ { privateKey && (
177
+ < button
178
+ onClick = { ( ) => handleCopy ( privateKey , "Private key" ) }
179
+ className = "absolute top-2 right-2 text-gray-400 hover:text-white"
180
+ >
181
+ < FaCopy />
182
+ </ button >
183
+ ) }
184
+ </ div >
185
+ </ div >
186
+ </ div >
187
+ </ div >
188
+
189
+ { /* Change Password Section */ }
190
+ < div className = "mb-8" >
191
+ < h2 className = "text-xl font-semibold mb-4 text-white" > Change Password</ h2 >
192
+ < form onSubmit = { handlePasswordChange } className = "space-y-4" >
193
+ < div >
194
+ < label className = "block text-sm font-medium text-gray-300 mb-2" > Current Password</ label >
195
+ < div className = "relative" >
196
+ < input
197
+ type = { showCurrentPassword ? "text" : "password" }
198
+ value = { currentPassword }
199
+ onChange = { ( e ) => setCurrentPassword ( e . target . value ) }
200
+ className = "w-full p-2 bg-gray-900 border border-gray-700 rounded text-white"
201
+ />
202
+ < button
203
+ type = "button"
204
+ onClick = { ( ) => setShowCurrentPassword ( ! showCurrentPassword ) }
205
+ className = "absolute right-2 top-2.5 text-gray-400 hover:text-white"
206
+ >
207
+ { showCurrentPassword ? < FaEyeSlash /> : < FaEye /> }
208
+ </ button >
209
+ </ div >
210
+ </ div >
211
+
212
+ < div >
213
+ < label className = "block text-sm font-medium text-gray-300 mb-2" > New Password</ label >
214
+ < div className = "relative" >
215
+ < input
216
+ type = { showNewPassword ? "text" : "password" }
217
+ value = { newPassword }
218
+ onChange = { ( e ) => setNewPassword ( e . target . value ) }
219
+ className = "w-full p-2 bg-gray-900 border border-gray-700 rounded text-white"
220
+ />
221
+ < button
222
+ type = "button"
223
+ onClick = { ( ) => setShowNewPassword ( ! showNewPassword ) }
224
+ className = "absolute right-2 top-2.5 text-gray-400 hover:text-white"
225
+ >
226
+ { showNewPassword ? < FaEyeSlash /> : < FaEye /> }
227
+ </ button >
228
+ </ div >
229
+ </ div >
230
+
231
+ < div >
232
+ < label className = "block text-sm font-medium text-gray-300 mb-2" > Confirm New Password</ label >
233
+ < div className = "relative" >
234
+ < input
235
+ type = { showConfirmPassword ? "text" : "password" }
236
+ value = { confirmPassword }
237
+ onChange = { ( e ) => setConfirmPassword ( e . target . value ) }
238
+ className = "w-full p-2 bg-gray-900 border border-gray-700 rounded text-white"
239
+ />
240
+ < button
241
+ type = "button"
242
+ onClick = { ( ) => setShowConfirmPassword ( ! showConfirmPassword ) }
243
+ className = "absolute right-2 top-2.5 text-gray-400 hover:text-white"
244
+ >
245
+ { showConfirmPassword ? < FaEyeSlash /> : < FaEye /> }
246
+ </ button >
247
+ </ div >
248
+ </ div >
249
+
250
+ < button
251
+ type = "submit"
252
+ disabled = { loading }
253
+ className = "w-full bg-blue-600 hover:bg-blue-700 text-white py-2 rounded-lg transition"
254
+ >
255
+ { loading ? "Updating..." : "Update Password" }
256
+ </ button >
257
+ </ form >
258
+ </ div >
259
+
260
+ { /* Delete Account Section */ }
261
+ < div >
262
+ < h2 className = "text-xl font-semibold mb-4 text-white flex items-center" >
263
+ < FaTrash className = "w-5 h-5 mr-2" />
264
+ Delete Account
265
+ </ h2 >
266
+
267
+ { ! showDeleteConfirm ? (
268
+ < button
269
+ onClick = { ( ) => setShowDeleteConfirm ( true ) }
270
+ className = "w-full bg-red-600 hover:bg-red-700 text-white py-2 rounded-lg transition flex items-center justify-center"
271
+ >
272
+ < FaExclamationTriangle className = "mr-2" />
273
+ Delete Account
274
+ </ button >
275
+ ) : (
276
+ < div className = "space-y-4" >
277
+ < p className = "text-red-400 text-sm" >
278
+ This action cannot be undone. Please enter your password to confirm.
279
+ </ p >
280
+ < div className = "relative" >
281
+ < input
282
+ type = { showCurrentPassword ? "text" : "password" }
283
+ value = { currentPassword }
284
+ onChange = { ( e ) => setCurrentPassword ( e . target . value ) }
285
+ placeholder = "Enter your password"
286
+ className = "w-full p-2 bg-gray-900 border border-gray-700 rounded text-white"
287
+ />
288
+ < button
289
+ type = "button"
290
+ onClick = { ( ) => setShowCurrentPassword ( ! showCurrentPassword ) }
291
+ className = "absolute right-2 top-2.5 text-gray-400 hover:text-white"
292
+ >
293
+ { showCurrentPassword ? < FaEyeSlash /> : < FaEye /> }
294
+ </ button >
295
+ </ div >
296
+ < div className = "flex space-x-4" >
297
+ < button
298
+ onClick = { ( ) => {
299
+ setShowDeleteConfirm ( false ) ;
300
+ setCurrentPassword ( "" ) ;
301
+ } }
302
+ className = "flex-1 bg-gray-600 hover:bg-gray-700 text-white py-2 rounded-lg transition"
303
+ >
304
+ Cancel
305
+ </ button >
306
+ < button
307
+ onClick = { handleDeleteAccount }
308
+ disabled = { loading }
309
+ className = "flex-1 bg-red-600 hover:bg-red-700 text-white py-2 rounded-lg transition"
310
+ >
311
+ { loading ? "Deleting..." : "Confirm Delete" }
312
+ </ button >
313
+ </ div >
314
+ </ div >
315
+ ) }
316
+ </ div >
317
+ </ div >
318
+ </ div >
319
+ ) ;
320
+ }
0 commit comments