From 334a55f0a097fcfebcdd406994069b4a9938c7bb Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Wed, 28 Jan 2026 13:48:02 +0530 Subject: [PATCH] feat: Implement agent verification management, including document display and status updates, on the user detail page. --- src/app/dashboard/users/[id]/page.tsx | 333 ++++++++++++++++++++++++++ src/services/index.ts | 3 + src/services/users.service.ts | 47 ++++ 3 files changed, 383 insertions(+) diff --git a/src/app/dashboard/users/[id]/page.tsx b/src/app/dashboard/users/[id]/page.tsx index 947964e..f5c1c6f 100644 --- a/src/app/dashboard/users/[id]/page.tsx +++ b/src/app/dashboard/users/[id]/page.tsx @@ -9,6 +9,8 @@ import { uploadService, agentTypesService, AgentType, + VerificationStatus, + VerificationDocument, } from '@/services'; export default function UserDetailPage() { @@ -27,6 +29,12 @@ export default function UserDetailPage() { const [isUpdatingAgentType, setIsUpdatingAgentType] = useState(false); const [updateSuccess, setUpdateSuccess] = useState(''); + // Verification + const [verificationDocuments, setVerificationDocuments] = useState([]); + const [isLoadingDocuments, setIsLoadingDocuments] = useState(false); + const [verificationNote, setVerificationNote] = useState(''); + const [isUpdatingVerification, setIsUpdatingVerification] = useState(false); + // Helper function to check if avatar is an S3 key const isS3Key = (avatar: string | null | undefined): boolean => { if (!avatar) return false; @@ -38,6 +46,13 @@ export default function UserDetailPage() { fetchAgentTypes(); }, [userId]); + // Fetch verification documents when user is loaded and is an agent + useEffect(() => { + if (user && user.role === 'AGENT') { + fetchVerificationDocuments(); + } + }, [user?.id, user?.role]); + const fetchUser = async () => { setIsLoading(true); setError(''); @@ -84,6 +99,97 @@ export default function UserDetailPage() { } }; + const fetchVerificationDocuments = async () => { + if (!user || user.role !== 'AGENT') return; + + setIsLoadingDocuments(true); + try { + const docs = await usersService.getVerificationDocuments(userId); + // Fetch presigned URLs for each document + const docsWithUrls = await Promise.all( + docs.map(async (doc) => { + try { + const url = await uploadService.getPresignedDownloadUrl(doc.key); + return { ...doc, url }; + } catch { + return doc; + } + }) + ); + setVerificationDocuments(docsWithUrls); + } catch (err) { + console.error('Failed to fetch verification documents:', err); + } finally { + setIsLoadingDocuments(false); + } + }; + + const handleVerification = async (status: VerificationStatus) => { + if (!user) return; + + setIsUpdatingVerification(true); + setError(''); + setUpdateSuccess(''); + + try { + await usersService.updateVerification(user.id, { + status, + note: verificationNote || undefined, + }); + setUpdateSuccess( + status === 'APPROVED' + ? 'Agent verification approved successfully' + : 'Agent verification rejected' + ); + setVerificationNote(''); + // Refresh user data + await fetchUser(); + // Clear success message after 3 seconds + setTimeout(() => setUpdateSuccess(''), 3000); + } catch (err) { + setError(getErrorMessage(err)); + } finally { + setIsUpdatingVerification(false); + } + }; + + const getVerificationStatusBadge = (status: VerificationStatus | undefined) => { + switch (status) { + case 'APPROVED': + return ( + + Approved + + ); + case 'REJECTED': + return ( + + Rejected + + ); + case 'PENDING_REVIEW': + return ( + + Pending Review + + ); + default: + return ( + + Not Submitted + + ); + } + }; + + const formatFileSize = (bytes: number) => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + }; + const handleUpdateAgentType = async () => { if (!selectedAgentTypeId || !user) return; @@ -426,6 +532,233 @@ export default function UserDetailPage() { )} + + {/* Verification Section (only for AGENT role) */} + {user.role === 'AGENT' && user.agentProfile && ( +
+
+
+

Verification Status

+ {getVerificationStatusBadge(user.agentProfile.verificationStatus as VerificationStatus)} +
+
+
+ {/* Verification Info */} + {user.agentProfile.isVerified && user.agentProfile.verifiedAt && ( +
+

+ Verified on {formatDate(user.agentProfile.verifiedAt)} +

+
+ )} + + {user.agentProfile.verificationStatus === 'REJECTED' && user.agentProfile.verificationNote && ( +
+

Rejection Note:

+

{user.agentProfile.verificationNote}

+
+ )} + + {/* Uploaded Documents */} +
+

Uploaded Documents

+ {isLoadingDocuments ? ( +
+
+
+ ) : verificationDocuments.length > 0 ? ( +
+ {verificationDocuments.map((doc, index) => ( +
+
+ + + +
+

{doc.name}

+

{formatFileSize(doc.size)}

+
+
+ {doc.url && ( + + Download + + )} +
+ ))} +
+ ) : ( +

+ No documents uploaded yet +

+ )} +
+ + {/* Admin Actions */} + {user.agentProfile.verificationStatus !== 'APPROVED' && ( +
+

Admin Actions

+ + {/* Note Input */} +
+ +