'use client' import { Fragment, useState, useEffect } from 'react' import { Dialog, Transition, Listbox } from '@headlessui/react' import { XMarkIcon, UserIcon, DocumentTextIcon, ShieldCheckIcon, CalendarIcon, EnvelopeIcon, PhoneIcon, MapPinIcon, BuildingOfficeIcon, IdentificationIcon, CheckCircleIcon, XCircleIcon, ChevronUpDownIcon, CheckIcon } from '@heroicons/react/24/outline' import { AdminAPI, DetailedUserInfo } from '../utils/api' import useAuthStore from '../store/authStore' interface UserDetailModalProps { isOpen: boolean onClose: () => void userId: string | null onUserUpdated?: () => void } type UserStatus = 'inactive' | 'pending' | 'active' | 'suspended' | 'archived' const STATUS_OPTIONS: { value: UserStatus; label: string; color: string }[] = [ { value: 'pending', label: 'Pending', color: 'amber' }, { value: 'active', label: 'Active', color: 'green' }, { value: 'suspended', label: 'Suspended', color: 'rose' }, { value: 'archived', label: 'Archived', color: 'gray' }, { value: 'inactive', label: 'Inactive', color: 'gray' } ] export default function UserDetailModal({ isOpen, onClose, userId, onUserUpdated }: UserDetailModalProps) { const [userDetails, setUserDetails] = useState(null) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [saving, setSaving] = useState(false) const [selectedStatus, setSelectedStatus] = useState('pending') const token = useAuthStore(state => state.accessToken) // Contract preview state (lazy-loaded) const [previewLoading, setPreviewLoading] = useState(false) const [previewHtml, setPreviewHtml] = useState(null) const [previewError, setPreviewError] = useState(null) useEffect(() => { if (isOpen && userId && token) { fetchUserDetails() } }, [isOpen, userId, token]) useEffect(() => { if (userDetails?.userStatus?.status) { setSelectedStatus(userDetails.userStatus.status as UserStatus) } }, [userDetails]) const fetchUserDetails = async () => { if (!userId || !token) return setLoading(true) setError(null) try { const response = await AdminAPI.getDetailedUserInfo(token, userId) if (response.success) { setUserDetails(response) } else { throw new Error(response.message || 'Failed to fetch user details') } } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to fetch user details' setError(errorMessage) console.error('UserDetailModal.fetchUserDetails error:', err) } finally { setLoading(false) } } const handleStatusChange = async (newStatus: UserStatus) => { if (!userId || !token || newStatus === selectedStatus) return setSaving(true) setError(null) try { const response = await AdminAPI.updateUserStatus(token, userId, newStatus) if (response.success) { setSelectedStatus(newStatus) await fetchUserDetails() if (onUserUpdated) { onUserUpdated() } } else { throw new Error(response.message || 'Failed to update user status') } } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to update user status' setError(errorMessage) console.error('UserDetailModal.handleStatusChange error:', err) } finally { setSaving(false) } } const handleToggleAdminVerification = async () => { if (!userId || !token || !userDetails?.userStatus) return setSaving(true) setError(null) try { const newValue = userDetails.userStatus.is_admin_verified === 1 ? 0 : 1 const response = await AdminAPI.updateUserVerification(token, userId, newValue) if (response.success) { await fetchUserDetails() if (onUserUpdated) { onUserUpdated() } } else { throw new Error(response.message || 'Failed to update verification status') } } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to update verification status' setError(errorMessage) console.error('UserDetailModal.handleToggleAdminVerification error:', err) } finally { setSaving(false) } } const loadContractPreview = async () => { if (!userId || !token || !userDetails) return setPreviewLoading(true) setPreviewError(null) try { const html = await AdminAPI.getContractPreviewHtml(token, String(userId), userDetails.user.user_type) setPreviewHtml(html) } catch (e: any) { console.error('UserDetailModal.loadContractPreview error:', e) setPreviewError(e?.message || 'Failed to load contract preview') setPreviewHtml(null) } finally { setPreviewLoading(false) } } const formatDate = (dateString: string | undefined | null) => { if (!dateString) return 'N/A' return new Date(dateString).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) } const formatFileSize = (bytes: number | undefined) => { if (!bytes) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i] } const getStatusColor = (status: UserStatus) => { const option = STATUS_OPTIONS.find(opt => opt.value === status) return option?.color || 'gray' } const getStatusBadgeClass = (color: string) => { const colorMap: Record = { amber: 'bg-amber-100 text-amber-800 border-amber-200', green: 'bg-green-100 text-green-800 border-green-200', rose: 'bg-rose-100 text-rose-800 border-rose-200', gray: 'bg-gray-100 text-gray-800 border-gray-200' } return colorMap[color] || colorMap.gray } if (!isOpen) return null return (
{/* Close Button */}
{/* Scrollable Content Area */}
{loading ? (
) : error ? (
) : userDetails ? (
{/* Header Section with User Info & Status */}
{userDetails.user.user_type === 'company' ? ( ) : ( )}

{userDetails.user.user_type === 'personal' ? `${userDetails.personalProfile?.first_name || ''} ${userDetails.personalProfile?.last_name || ''}`.trim() : userDetails.companyProfile?.company_name || 'Unknown'}

{userDetails.user.email}

{userDetails.user.user_type === 'personal' ? 'Personal' : 'Company'} {userDetails.user.role === 'super_admin' ? 'Super Admin' : userDetails.user.role}
{/* Status Badge */} {userDetails.userStatus && (
Current Status
{userDetails.userStatus.status.charAt(0).toUpperCase() + userDetails.userStatus.status.slice(1)}
)}
{/* Admin Controls Section */}

Admin Controls

{/* Status Dropdown */}
{STATUS_OPTIONS.find(opt => opt.value === selectedStatus)?.label || selectedStatus} {STATUS_OPTIONS.map((option) => ( `relative cursor-pointer select-none py-2 pl-10 pr-4 ${ active ? 'bg-indigo-100 text-indigo-900' : 'text-gray-900' }` } value={option.value} > {({ selected }) => ( <> {option.label} {selected && ( )} )} ))}
{/* Admin Verification Toggle */}
{/* Contract Preview (admin verify flow) */}

Contract Preview

{previewError && (
{previewError}
)} {previewLoading && (
Loading preview…
)} {!previewLoading && previewHtml && (