@@ -16,6 +16,13 @@ import {
16
16
CardTitle ,
17
17
} from '@/components/ui/card' ;
18
18
import { TableIcon , LayoutGrid , RefreshCw } from 'lucide-react' ;
19
+ import {
20
+ Dialog ,
21
+ DialogContent ,
22
+ DialogDescription ,
23
+ DialogHeader ,
24
+ DialogTitle ,
25
+ } from '@/components/ui/dialog' ;
19
26
20
27
export default function AdminPage ( ) {
21
28
const [ adminToken , setAdminToken ] = useState ( '' ) ;
@@ -56,11 +63,27 @@ export default function AdminPage() {
56
63
setIsTokenValid ( true ) ;
57
64
} ;
58
65
66
+ const [ selectedUser , setSelectedUser ] = useState < any | null > ( null ) ;
67
+ const [ isUserDetailsOpen , setIsUserDetailsOpen ] = useState ( false ) ;
68
+
69
+ const {
70
+ data : userDetails ,
71
+ isLoading : isLoadingDetails ,
72
+ refetch : refetchUserDetails ,
73
+ } = api . admin . getUserDetails . useQuery (
74
+ {
75
+ adminToken,
76
+ privyDid : selectedUser ?. privyDid || '' ,
77
+ } ,
78
+ {
79
+ enabled : ! ! selectedUser && isUserDetailsOpen && isTokenValid ,
80
+ retry : false ,
81
+ } ,
82
+ ) ;
83
+
59
84
const handleUserClick = ( user : any ) => {
60
- // You can add logic here to show user details in a modal
61
- // or navigate to a user detail page
62
- console . log ( 'User clicked:' , user ) ;
63
- toast . info ( `Selected user: ${ user . email } ` ) ;
85
+ setSelectedUser ( user ) ;
86
+ setIsUserDetailsOpen ( true ) ;
64
87
} ;
65
88
66
89
const handleSyncKyc = async ( ) => {
@@ -186,6 +209,191 @@ export default function AdminPage() {
186
209
/>
187
210
</ TabsContent >
188
211
</ Tabs >
212
+
213
+ < Dialog open = { isUserDetailsOpen } onOpenChange = { setIsUserDetailsOpen } >
214
+ < DialogContent className = "max-w-2xl max-h-[80vh] overflow-y-auto" >
215
+ < DialogHeader >
216
+ < DialogTitle > User Details</ DialogTitle >
217
+ < DialogDescription >
218
+ Detailed information about { selectedUser ?. email }
219
+ </ DialogDescription >
220
+ </ DialogHeader >
221
+
222
+ { isLoadingDetails ? (
223
+ < div className = "py-8 text-center" > Loading user details...</ div >
224
+ ) : userDetails ? (
225
+ < div className = "space-y-6" >
226
+ < Card >
227
+ < CardHeader >
228
+ < CardTitle className = "text-lg" > Basic Information</ CardTitle >
229
+ </ CardHeader >
230
+ < CardContent className = "space-y-2 text-sm" >
231
+ < div className = "grid grid-cols-2 gap-2" >
232
+ < div >
233
+ < strong > Email:</ strong > { selectedUser ?. email }
234
+ </ div >
235
+ < div >
236
+ < strong > User Role:</ strong > { ' ' }
237
+ { userDetails . user . userRole || 'N/A' }
238
+ </ div >
239
+ < div >
240
+ < strong > First Name:</ strong > { ' ' }
241
+ { userDetails . user . firstName || 'N/A' }
242
+ </ div >
243
+ < div >
244
+ < strong > Last Name:</ strong > { ' ' }
245
+ { userDetails . user . lastName || 'N/A' }
246
+ </ div >
247
+ < div >
248
+ < strong > Company:</ strong > { ' ' }
249
+ { userDetails . user . companyName || 'N/A' }
250
+ </ div >
251
+ < div >
252
+ < strong > Beneficiary Type:</ strong > { ' ' }
253
+ { userDetails . user . beneficiaryType || 'N/A' }
254
+ </ div >
255
+ </ div >
256
+ </ CardContent >
257
+ </ Card >
258
+
259
+ < Card >
260
+ < CardHeader >
261
+ < CardTitle className = "text-lg" > Workspace</ CardTitle >
262
+ </ CardHeader >
263
+ < CardContent className = "space-y-2 text-sm" >
264
+ { userDetails . primaryWorkspace ? (
265
+ < >
266
+ < div >
267
+ < strong > Primary Workspace:</ strong > { ' ' }
268
+ { userDetails . primaryWorkspace . name }
269
+ </ div >
270
+ < div >
271
+ < strong > Workspace ID:</ strong > { ' ' }
272
+ { userDetails . primaryWorkspace . id }
273
+ </ div >
274
+ </ >
275
+ ) : (
276
+ < div className = "text-muted-foreground" >
277
+ No primary workspace
278
+ </ div >
279
+ ) }
280
+
281
+ { userDetails . workspaceMemberships . length > 0 && (
282
+ < div className = "mt-4" >
283
+ < strong > All Workspaces:</ strong >
284
+ < ul className = "list-disc list-inside mt-2 space-y-1" >
285
+ { userDetails . workspaceMemberships . map ( ( wm : any ) => (
286
+ < li key = { wm . workspaceId } >
287
+ { wm . workspaceName } ({ wm . role } )
288
+ { wm . isPrimary && ' - Primary' }
289
+ </ li >
290
+ ) ) }
291
+ </ ul >
292
+ </ div >
293
+ ) }
294
+ </ CardContent >
295
+ </ Card >
296
+
297
+ < Card >
298
+ < CardHeader >
299
+ < CardTitle className = "text-lg" > Features</ CardTitle >
300
+ </ CardHeader >
301
+ < CardContent className = "space-y-2 text-sm" >
302
+ < div >
303
+ < strong > Savings Enabled:</ strong > { ' ' }
304
+ < span
305
+ className = {
306
+ userDetails . hasSavings
307
+ ? 'text-green-600'
308
+ : 'text-red-600'
309
+ }
310
+ >
311
+ { userDetails . hasSavings ? 'Yes' : 'No' }
312
+ </ span >
313
+ </ div >
314
+
315
+ { userDetails . features . length > 0 ? (
316
+ < div className = "mt-4" >
317
+ < strong > All Features:</ strong >
318
+ < ul className = "list-disc list-inside mt-2 space-y-1" >
319
+ { userDetails . features . map ( ( f : any ) => (
320
+ < li key = { f . featureName } >
321
+ { f . featureName } -{ ' ' }
322
+ { f . isActive ? 'Active' : 'Inactive' }
323
+ { f . purchaseSource && ` (${ f . purchaseSource } )` }
324
+ </ li >
325
+ ) ) }
326
+ </ ul >
327
+ </ div >
328
+ ) : (
329
+ < div className = "mt-2 text-muted-foreground" >
330
+ No features enabled
331
+ </ div >
332
+ ) }
333
+ </ CardContent >
334
+ </ Card >
335
+
336
+ < Card >
337
+ < CardHeader >
338
+ < CardTitle className = "text-lg" > Account Status</ CardTitle >
339
+ </ CardHeader >
340
+ < CardContent className = "space-y-2 text-sm" >
341
+ < div className = "grid grid-cols-2 gap-2" >
342
+ < div >
343
+ < strong > KYC Status:</ strong > { ' ' }
344
+ < span
345
+ className = {
346
+ userDetails . user . kycStatus === 'approved'
347
+ ? 'text-green-600'
348
+ : userDetails . user . kycStatus === 'rejected'
349
+ ? 'text-red-600'
350
+ : userDetails . user . kycStatus === 'pending'
351
+ ? 'text-yellow-600'
352
+ : ''
353
+ }
354
+ >
355
+ { userDetails . user . kycStatus || 'N/A' }
356
+ </ span >
357
+ </ div >
358
+ < div >
359
+ < strong > KYC Provider:</ strong > { ' ' }
360
+ { userDetails . user . kycProvider || 'N/A' }
361
+ </ div >
362
+ < div >
363
+ < strong > Align Customer ID:</ strong > { ' ' }
364
+ { userDetails . user . alignCustomerId || 'N/A' }
365
+ </ div >
366
+ < div >
367
+ < strong > Virtual Account:</ strong > { ' ' }
368
+ { userDetails . user . alignVirtualAccountId || 'N/A' }
369
+ </ div >
370
+ < div >
371
+ < strong > Loops Synced:</ strong > { ' ' }
372
+ { userDetails . user . loopsContactSynced ? 'Yes' : 'No' }
373
+ </ div >
374
+ < div >
375
+ < strong > Created At:</ strong > { ' ' }
376
+ { new Date (
377
+ userDetails . user . createdAt ,
378
+ ) . toLocaleDateString ( ) }
379
+ </ div >
380
+ </ div >
381
+ </ CardContent >
382
+ </ Card >
383
+
384
+ < div className = "flex justify-end" >
385
+ < Button onClick = { ( ) => refetchUserDetails ( ) } size = "sm" >
386
+ Refresh Details
387
+ </ Button >
388
+ </ div >
389
+ </ div >
390
+ ) : (
391
+ < div className = "py-8 text-center text-muted-foreground" >
392
+ No details available
393
+ </ div >
394
+ ) }
395
+ </ DialogContent >
396
+ </ Dialog >
189
397
</ div >
190
398
) ;
191
399
}
0 commit comments