feat: Add agent verification history display to the user detail page.
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
|||||||
AgentType,
|
AgentType,
|
||||||
VerificationStatus,
|
VerificationStatus,
|
||||||
VerificationDocument,
|
VerificationDocument,
|
||||||
|
VerificationHistoryEntry,
|
||||||
} from '@/services';
|
} from '@/services';
|
||||||
|
|
||||||
export default function UserDetailPage() {
|
export default function UserDetailPage() {
|
||||||
@@ -34,6 +35,7 @@ export default function UserDetailPage() {
|
|||||||
const [isLoadingDocuments, setIsLoadingDocuments] = useState(false);
|
const [isLoadingDocuments, setIsLoadingDocuments] = useState(false);
|
||||||
const [verificationNote, setVerificationNote] = useState('');
|
const [verificationNote, setVerificationNote] = useState('');
|
||||||
const [isUpdatingVerification, setIsUpdatingVerification] = useState(false);
|
const [isUpdatingVerification, setIsUpdatingVerification] = useState(false);
|
||||||
|
const [verificationHistory, setVerificationHistory] = useState<VerificationHistoryEntry[]>([]);
|
||||||
|
|
||||||
// Helper function to check if avatar is an S3 key
|
// Helper function to check if avatar is an S3 key
|
||||||
const isS3Key = (avatar: string | null | undefined): boolean => {
|
const isS3Key = (avatar: string | null | undefined): boolean => {
|
||||||
@@ -50,6 +52,7 @@ export default function UserDetailPage() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user && user.role === 'AGENT') {
|
if (user && user.role === 'AGENT') {
|
||||||
fetchVerificationDocuments();
|
fetchVerificationDocuments();
|
||||||
|
fetchVerificationHistory();
|
||||||
}
|
}
|
||||||
}, [user?.id, user?.role]);
|
}, [user?.id, user?.role]);
|
||||||
|
|
||||||
@@ -131,6 +134,15 @@ export default function UserDetailPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchVerificationHistory = async () => {
|
||||||
|
try {
|
||||||
|
const history = await usersService.getVerificationHistory(userId);
|
||||||
|
setVerificationHistory(history);
|
||||||
|
} catch {
|
||||||
|
// History may not exist yet
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleVerification = async (status: VerificationStatus) => {
|
const handleVerification = async (status: VerificationStatus) => {
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
@@ -149,8 +161,9 @@ export default function UserDetailPage() {
|
|||||||
: 'Agent verification rejected'
|
: 'Agent verification rejected'
|
||||||
);
|
);
|
||||||
setVerificationNote('');
|
setVerificationNote('');
|
||||||
// Refresh user data
|
// Refresh user data and history
|
||||||
await fetchUser();
|
await fetchUser();
|
||||||
|
await fetchVerificationHistory();
|
||||||
// Clear success message after 3 seconds
|
// Clear success message after 3 seconds
|
||||||
setTimeout(() => setUpdateSuccess(''), 3000);
|
setTimeout(() => setUpdateSuccess(''), 3000);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -763,6 +776,44 @@ export default function UserDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Verification History */}
|
||||||
|
{verificationHistory.length > 0 && (
|
||||||
|
<div className="border-t border-[#e5e7eb] pt-6">
|
||||||
|
<h3 className="text-sm font-medium text-[#00293d] mb-3">Verification History</h3>
|
||||||
|
<div className="space-y-3">
|
||||||
|
{verificationHistory.map((entry) => (
|
||||||
|
<div key={entry.id} className="flex items-start gap-3 p-3 bg-gray-50 rounded-lg">
|
||||||
|
<div className={`w-2 h-2 rounded-full mt-1.5 flex-shrink-0 ${
|
||||||
|
entry.status === 'APPROVED' ? 'bg-green-500' :
|
||||||
|
entry.status === 'REJECTED' ? 'bg-red-500' :
|
||||||
|
entry.status === 'PENDING_REVIEW' ? 'bg-yellow-500' : 'bg-gray-400'
|
||||||
|
}`} />
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className={`px-2 py-0.5 text-xs font-semibold rounded-full ${
|
||||||
|
entry.status === 'APPROVED' ? 'bg-green-100 text-green-800' :
|
||||||
|
entry.status === 'REJECTED' ? 'bg-red-100 text-red-800' :
|
||||||
|
entry.status === 'PENDING_REVIEW' ? 'bg-yellow-100 text-yellow-800' : 'bg-gray-100 text-gray-800'
|
||||||
|
}`}>
|
||||||
|
{entry.status.replace('_', ' ')}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-gray-500">
|
||||||
|
{new Date(entry.createdAt).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{entry.note && (
|
||||||
|
<p className="text-sm text-gray-700 mt-1">{entry.note}</p>
|
||||||
|
)}
|
||||||
|
{entry.admin && (
|
||||||
|
<p className="text-xs text-gray-500 mt-1">By: {entry.admin.email}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export type {
|
|||||||
UserFilters,
|
UserFilters,
|
||||||
VerificationStatus,
|
VerificationStatus,
|
||||||
VerificationDocument,
|
VerificationDocument,
|
||||||
|
VerificationHistoryEntry,
|
||||||
AgentProfileDetails,
|
AgentProfileDetails,
|
||||||
} from './users.service';
|
} from './users.service';
|
||||||
|
|
||||||
|
|||||||
@@ -150,6 +150,21 @@ class UsersService {
|
|||||||
}>>(`${this.basePath}/${userId}/verification`, data);
|
}>>(`${this.basePath}/${userId}/verification`, data);
|
||||||
return response.data.data;
|
return response.data.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getVerificationHistory(userId: string): Promise<VerificationHistoryEntry[]> {
|
||||||
|
const response = await api.get<ApiResponse<VerificationHistoryEntry[]>>(`${this.basePath}/${userId}/verification-history`);
|
||||||
|
return response.data.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VerificationHistoryEntry {
|
||||||
|
id: string;
|
||||||
|
agentProfileId: string;
|
||||||
|
status: VerificationStatus;
|
||||||
|
note: string | null;
|
||||||
|
adminId: string | null;
|
||||||
|
createdAt: string;
|
||||||
|
admin: { id: string; email: string } | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const usersService = new UsersService();
|
export const usersService = new UsersService();
|
||||||
|
|||||||
Reference in New Issue
Block a user