feat: Add Source Serif 4 and Fractul font families and integrate them across the admin UI.
This commit is contained in:
@@ -234,7 +234,7 @@ export default function UserDetailPage() {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -247,7 +247,7 @@ export default function UserDetailPage() {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => router.push('/dashboard/users')}
|
||||
className="mt-4 px-4 py-2 text-blue-600 hover:text-blue-800"
|
||||
className="mt-4 px-4 py-2 text-[#f5a623] hover:text-[#e09620]"
|
||||
>
|
||||
← Back to Users
|
||||
</button>
|
||||
@@ -258,10 +258,10 @@ export default function UserDetailPage() {
|
||||
if (!user) {
|
||||
return (
|
||||
<div className="p-6">
|
||||
<p className="text-gray-500">User not found</p>
|
||||
<p className="text-[#666666] font-serif">User not found</p>
|
||||
<button
|
||||
onClick={() => router.push('/dashboard/users')}
|
||||
className="mt-4 px-4 py-2 text-blue-600 hover:text-blue-800"
|
||||
className="mt-4 px-4 py-2 text-[#f5a623] hover:text-[#e09620]"
|
||||
>
|
||||
← Back to Users
|
||||
</button>
|
||||
@@ -274,7 +274,7 @@ export default function UserDetailPage() {
|
||||
{/* Back Button */}
|
||||
<button
|
||||
onClick={() => router.push('/dashboard/users')}
|
||||
className="mb-6 flex items-center text-gray-600 hover:text-gray-900"
|
||||
className="mb-6 flex items-center text-[#666666] hover:text-[#00293d]"
|
||||
>
|
||||
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
@@ -295,7 +295,7 @@ export default function UserDetailPage() {
|
||||
)}
|
||||
|
||||
{/* User Header */}
|
||||
<div className="bg-white rounded-lg shadow mb-6">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
|
||||
<div className="p-6">
|
||||
<div className="flex items-center">
|
||||
{/* Avatar */}
|
||||
@@ -303,8 +303,8 @@ export default function UserDetailPage() {
|
||||
{avatarUrl ? (
|
||||
<img className="h-20 w-20 rounded-full object-cover" src={avatarUrl} alt="" />
|
||||
) : (
|
||||
<div className="h-20 w-20 rounded-full bg-gray-200 flex items-center justify-center">
|
||||
<span className="text-gray-500 font-medium text-2xl">
|
||||
<div className="h-20 w-20 rounded-full bg-[#e8f0ee] flex items-center justify-center">
|
||||
<span className="text-[#666666] font-medium text-2xl">
|
||||
{user.profile?.firstName?.[0] || user.email[0].toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
@@ -313,10 +313,10 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Name & Email */}
|
||||
<div className="ml-6 flex-1">
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">
|
||||
{user.profile?.firstName} {user.profile?.lastName}
|
||||
</h1>
|
||||
<p className="text-gray-500">{user.email}</p>
|
||||
<p className="text-[#666666] font-serif">{user.email}</p>
|
||||
</div>
|
||||
|
||||
{/* Role & Status Badges */}
|
||||
@@ -326,8 +326,8 @@ export default function UserDetailPage() {
|
||||
user.role === 'ADMIN'
|
||||
? 'bg-red-100 text-red-800'
|
||||
: user.role === 'AGENT'
|
||||
? 'bg-purple-100 text-purple-800'
|
||||
: 'bg-blue-100 text-blue-800'
|
||||
? 'bg-[#fff7ed] text-[#f5a623]'
|
||||
: 'bg-[#e8f0ee] text-[#5ba4a4]'
|
||||
}`}
|
||||
>
|
||||
{user.role}
|
||||
@@ -349,20 +349,20 @@ export default function UserDetailPage() {
|
||||
</div>
|
||||
|
||||
{/* Basic Information */}
|
||||
<div className="bg-white rounded-lg shadow mb-6">
|
||||
<div className="px-6 py-4 border-b border-gray-200">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Basic Information</h2>
|
||||
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
|
||||
<div className="px-6 py-4 border-b border-[#e5e7eb]">
|
||||
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Basic Information</h2>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Provider</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Provider</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.authProvider === 'LOCAL' ? 'Email' : user.authProvider}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Email Verified</p>
|
||||
<p className="text-sm text-[#666666]">Email Verified</p>
|
||||
<div className="flex items-center">
|
||||
{user.emailVerified ? (
|
||||
<>
|
||||
@@ -377,25 +377,25 @@ export default function UserDetailPage() {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<svg className="w-5 h-5 text-gray-400 mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<svg className="w-5 h-5 text-[#9ca3af] mr-1" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span className="text-sm font-medium text-gray-500">Not Verified</span>
|
||||
<span className="text-sm font-medium text-[#666666]">Not Verified</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Joined</p>
|
||||
<p className="text-sm font-medium text-gray-900">{formatDate(user.createdAt)}</p>
|
||||
<p className="text-sm text-[#666666]">Joined</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">{formatDate(user.createdAt)}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Last Login</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Last Login</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.lastLoginAt ? formatDate(user.lastLoginAt) : 'Never'}
|
||||
</p>
|
||||
</div>
|
||||
@@ -404,33 +404,33 @@ export default function UserDetailPage() {
|
||||
</div>
|
||||
|
||||
{/* Contact Information */}
|
||||
<div className="bg-white rounded-lg shadow mb-6">
|
||||
<div className="px-6 py-4 border-b border-gray-200">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Contact Information</h2>
|
||||
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
|
||||
<div className="px-6 py-4 border-b border-[#e5e7eb]">
|
||||
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Contact Information</h2>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Phone</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Phone</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.profile?.phone || 'Not provided'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">City</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">City</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.profile?.city || 'Not provided'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">State</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">State</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.profile?.state || 'Not provided'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Country</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Country</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.profile?.country || 'Not provided'}
|
||||
</p>
|
||||
</div>
|
||||
@@ -440,21 +440,21 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Agent Details (only for AGENT role) */}
|
||||
{user.role === 'AGENT' && user.agentProfile && (
|
||||
<div className="bg-white rounded-lg shadow mb-6">
|
||||
<div className="px-6 py-4 border-b border-gray-200">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Agent Details</h2>
|
||||
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
|
||||
<div className="px-6 py-4 border-b border-[#e5e7eb]">
|
||||
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Agent Details</h2>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
{/* Agent Details Grid */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
{/* Agent Type - Editable */}
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 mb-1">Agent Type</p>
|
||||
<p className="text-sm text-[#666666] mb-1">Agent Type</p>
|
||||
<div className="flex items-center space-x-2">
|
||||
<select
|
||||
value={selectedAgentTypeId}
|
||||
onChange={(e) => setSelectedAgentTypeId(e.target.value)}
|
||||
className="w-full max-w-[160px] px-2 py-1 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white"
|
||||
className="w-full max-w-[160px] px-2 py-1 text-sm border border-[#e5e7eb] rounded-lg focus:ring-1 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white"
|
||||
>
|
||||
<option value="">Select...</option>
|
||||
{agentTypes.map((type) => (
|
||||
@@ -467,7 +467,7 @@ export default function UserDetailPage() {
|
||||
<button
|
||||
onClick={handleUpdateAgentType}
|
||||
disabled={isUpdatingAgentType}
|
||||
className="px-2 py-1 bg-blue-600 hover:bg-blue-700 text-white text-xs font-medium rounded transition-colors disabled:opacity-50"
|
||||
className="px-2 py-1 bg-[#f5a623] hover:bg-[#e09620] text-white text-xs font-medium rounded transition-colors disabled:opacity-50"
|
||||
>
|
||||
{isUpdatingAgentType ? '...' : 'Save'}
|
||||
</button>
|
||||
@@ -475,32 +475,32 @@ export default function UserDetailPage() {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Slug</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Slug</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.agentProfile.slug || 'Not set'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Company</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Company</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.agentProfile.companyName || 'Not provided'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">License Number</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">License Number</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.agentProfile.licenseNumber || 'Not provided'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Years of Experience</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Years of Experience</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.agentProfile.yearsOfExperience ?? 'Not provided'}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500">Profile Status</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666]">Profile Status</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.agentProfile.profileCompleteness}%{' '}
|
||||
<span
|
||||
className={`ml-1 inline-flex px-2 py-0.5 text-xs font-semibold rounded-full ${
|
||||
@@ -517,19 +517,19 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Headline & Bio */}
|
||||
{(user.agentProfile.headline || user.agentProfile.bio) && (
|
||||
<div className="mt-6 pt-6 border-t border-gray-200">
|
||||
<div className="mt-6 pt-6 border-t border-[#e5e7eb]">
|
||||
{user.agentProfile.headline && (
|
||||
<div className="mb-4">
|
||||
<p className="text-sm text-gray-500 mb-1">Headline</p>
|
||||
<p className="text-sm font-medium text-gray-900">
|
||||
<p className="text-sm text-[#666666] mb-1">Headline</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">
|
||||
{user.agentProfile.headline}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{user.agentProfile.bio && (
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 mb-1">Bio</p>
|
||||
<p className="text-sm text-gray-700 whitespace-pre-wrap">
|
||||
<p className="text-sm text-[#666666] mb-1">Bio</p>
|
||||
<p className="text-sm text-[#666666] whitespace-pre-wrap">
|
||||
{user.agentProfile.bio}
|
||||
</p>
|
||||
</div>
|
||||
@@ -542,10 +542,10 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Verification Section (only for AGENT role) */}
|
||||
{user.role === 'AGENT' && user.agentProfile && (
|
||||
<div className="bg-white rounded-lg shadow mb-6">
|
||||
<div className="px-6 py-4 border-b border-gray-200">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb] mb-6">
|
||||
<div className="px-6 py-4 border-b border-[#e5e7eb]">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-lg font-semibold text-gray-900">Verification Status</h2>
|
||||
<h2 className="text-lg font-semibold text-[#00293d] font-fractul">Verification Status</h2>
|
||||
{getVerificationStatusBadge(user.agentProfile.verificationStatus as VerificationStatus)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -568,21 +568,21 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Uploaded Documents */}
|
||||
<div className="mb-6">
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-3">Uploaded Documents</h3>
|
||||
<h3 className="text-sm font-medium text-[#00293d] mb-3">Uploaded Documents</h3>
|
||||
{isLoadingDocuments ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-blue-600"></div>
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-[#f5a623]"></div>
|
||||
</div>
|
||||
) : verificationDocuments.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
{verificationDocuments.map((doc, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex items-center justify-between p-3 bg-gray-50 border border-gray-200 rounded-lg"
|
||||
className="flex items-center justify-between p-3 bg-[#f5f9f8] border border-[#e5e7eb] rounded-xl"
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<svg
|
||||
className="w-8 h-8 text-gray-400 mr-3"
|
||||
className="w-8 h-8 text-[#9ca3af] mr-3"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -595,8 +595,8 @@ export default function UserDetailPage() {
|
||||
/>
|
||||
</svg>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-gray-900">{doc.name}</p>
|
||||
<p className="text-xs text-gray-500">{formatFileSize(doc.size)}</p>
|
||||
<p className="text-sm font-medium text-[#00293d]">{doc.name}</p>
|
||||
<p className="text-xs text-[#666666]">{formatFileSize(doc.size)}</p>
|
||||
</div>
|
||||
</div>
|
||||
{doc.url && (
|
||||
@@ -604,7 +604,7 @@ export default function UserDetailPage() {
|
||||
href={doc.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="px-3 py-1 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded transition-colors"
|
||||
className="px-3 py-1 text-sm text-[#f5a623] hover:text-[#e09620] hover:bg-[#fff7ed] rounded transition-colors"
|
||||
>
|
||||
Download
|
||||
</a>
|
||||
@@ -613,7 +613,7 @@ export default function UserDetailPage() {
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-gray-500 py-4 text-center">
|
||||
<p className="text-sm text-[#666666] py-4 text-center">
|
||||
No documents uploaded yet
|
||||
</p>
|
||||
)}
|
||||
@@ -621,19 +621,19 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Admin Actions */}
|
||||
{user.agentProfile.verificationStatus !== 'APPROVED' && (
|
||||
<div className="border-t border-gray-200 pt-6">
|
||||
<h3 className="text-sm font-medium text-gray-900 mb-3">Admin Actions</h3>
|
||||
<div className="border-t border-[#e5e7eb] pt-6">
|
||||
<h3 className="text-sm font-medium text-[#00293d] mb-3">Admin Actions</h3>
|
||||
|
||||
{/* Note Input */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-sm text-gray-500 mb-1">
|
||||
<label className="block text-sm text-[#666666] mb-1">
|
||||
Note (optional, required for rejection)
|
||||
</label>
|
||||
<textarea
|
||||
value={verificationNote}
|
||||
onChange={(e) => setVerificationNote(e.target.value)}
|
||||
rows={2}
|
||||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-900 bg-white placeholder:text-gray-500"
|
||||
className="w-full px-3 py-2 border border-[#e5e7eb] rounded-xl focus:ring-2 focus:ring-[#f5a623] focus:border-[#f5a623] text-[#00293d] bg-white placeholder:text-[#666666]"
|
||||
placeholder="Enter a note for the agent (e.g., rejection reason)"
|
||||
/>
|
||||
</div>
|
||||
@@ -736,7 +736,7 @@ export default function UserDetailPage() {
|
||||
</button>
|
||||
</div>
|
||||
{!verificationNote.trim() && (
|
||||
<p className="mt-2 text-xs text-gray-500">
|
||||
<p className="mt-2 text-xs text-[#666666]">
|
||||
* A note is required to reject verification
|
||||
</p>
|
||||
)}
|
||||
@@ -750,7 +750,7 @@ export default function UserDetailPage() {
|
||||
|
||||
{/* Already Approved Message */}
|
||||
{user.agentProfile.verificationStatus === 'APPROVED' && (
|
||||
<div className="border-t border-gray-200 pt-6">
|
||||
<div className="border-t border-[#e5e7eb] pt-6">
|
||||
<div className="flex items-center text-green-600">
|
||||
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path
|
||||
|
||||
@@ -96,24 +96,24 @@ export default function UsersPage() {
|
||||
<div>
|
||||
{/* Page Title */}
|
||||
<div className="mb-6">
|
||||
<h1 className="text-2xl font-bold text-gray-900">Users</h1>
|
||||
<p className="text-gray-500">Manage all registered users</p>
|
||||
<h1 className="text-2xl font-bold text-[#00293d] font-fractul">Users</h1>
|
||||
<p className="text-[#666666] font-serif">Manage all registered users</p>
|
||||
</div>
|
||||
|
||||
{/* Users Table */}
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="px-6 py-4 border-b border-gray-200">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-[#e5e7eb]">
|
||||
<div className="px-6 py-4 border-b border-[#e5e7eb]">
|
||||
<div className="flex justify-between items-center">
|
||||
{/* Filter Tabs */}
|
||||
<div className="flex space-x-1 bg-gray-100 rounded-lg p-1">
|
||||
<div className="flex space-x-1 bg-[#f5f9f8] rounded-lg p-1">
|
||||
{roleFilters.map((filter) => (
|
||||
<button
|
||||
key={filter.value}
|
||||
onClick={() => handleFilterChange(filter.value)}
|
||||
className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
|
||||
activeFilter === filter.value
|
||||
? 'bg-white text-gray-900 shadow-sm'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
? 'bg-white text-[#00293d] shadow-sm'
|
||||
: 'text-[#666666] hover:text-[#00293d]'
|
||||
}`}
|
||||
>
|
||||
{filter.label}
|
||||
@@ -122,7 +122,7 @@ export default function UsersPage() {
|
||||
</div>
|
||||
<button
|
||||
onClick={fetchUsers}
|
||||
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors flex items-center"
|
||||
className="px-4 py-2 bg-[#f5a623] hover:bg-[#e09620] text-white text-sm font-medium rounded-lg transition-colors flex items-center"
|
||||
>
|
||||
<svg
|
||||
className="w-4 h-4 mr-2"
|
||||
@@ -151,12 +151,12 @@ export default function UsersPage() {
|
||||
<div className="overflow-x-auto">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-[#f5a623]"></div>
|
||||
</div>
|
||||
) : users.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<svg
|
||||
className="mx-auto h-12 w-12 text-gray-400"
|
||||
className="mx-auto h-12 w-12 text-[#9ca3af]"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@@ -168,40 +168,40 @@ export default function UsersPage() {
|
||||
d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"
|
||||
/>
|
||||
</svg>
|
||||
<p className="mt-2 text-gray-500">No users found</p>
|
||||
<p className="mt-2 text-[#666666]">No users found</p>
|
||||
</div>
|
||||
) : (
|
||||
<table className="min-w-full divide-y divide-gray-200">
|
||||
<thead className="bg-gray-50">
|
||||
<table className="min-w-full divide-y divide-[#e5e7eb]">
|
||||
<thead className="bg-[#f5f9f8]">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
|
||||
User
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
|
||||
Role
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
|
||||
Status
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
|
||||
Provider
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
|
||||
Verified
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
|
||||
Joined
|
||||
</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-[#666666] uppercase tracking-wider">
|
||||
Last Login
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white divide-y divide-gray-200">
|
||||
<tbody className="bg-white divide-y divide-[#e5e7eb]">
|
||||
{users.map((user) => (
|
||||
<tr
|
||||
key={user.id}
|
||||
className="hover:bg-gray-50 cursor-pointer"
|
||||
className="hover:bg-[#f5f9f8] cursor-pointer"
|
||||
onClick={() => router.push(`/dashboard/users/${user.id}`)}
|
||||
>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
@@ -214,18 +214,18 @@ export default function UsersPage() {
|
||||
alt=""
|
||||
/>
|
||||
) : (
|
||||
<div className="h-10 w-10 rounded-full bg-gray-200 flex items-center justify-center">
|
||||
<span className="text-gray-500 font-medium text-sm">
|
||||
<div className="h-10 w-10 rounded-full bg-[#e8f0ee] flex items-center justify-center">
|
||||
<span className="text-[#666666] font-medium text-sm">
|
||||
{user.profile?.firstName?.[0] || user.email[0].toUpperCase()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
<div className="text-sm font-medium text-[#00293d]">
|
||||
{user.profile?.firstName} {user.profile?.lastName}
|
||||
</div>
|
||||
<div className="text-sm text-gray-500">{user.email}</div>
|
||||
<div className="text-sm text-[#666666]">{user.email}</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
@@ -235,8 +235,8 @@ export default function UsersPage() {
|
||||
user.role === 'ADMIN'
|
||||
? 'bg-red-100 text-red-800'
|
||||
: user.role === 'AGENT'
|
||||
? 'bg-purple-100 text-purple-800'
|
||||
: 'bg-blue-100 text-blue-800'
|
||||
? 'bg-[#fff7ed] text-[#f5a623]'
|
||||
: 'bg-[#e8f0ee] text-[#5ba4a4]'
|
||||
}`}
|
||||
>
|
||||
{user.role}
|
||||
@@ -257,7 +257,7 @@ export default function UsersPage() {
|
||||
{user.status === 'PENDING_VERIFICATION' ? 'PENDING' : user.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-[#666666]">
|
||||
{user.authProvider === 'LOCAL' ? 'Email' : user.authProvider}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap">
|
||||
@@ -275,7 +275,7 @@ export default function UsersPage() {
|
||||
</svg>
|
||||
) : (
|
||||
<svg
|
||||
className="w-5 h-5 text-gray-400"
|
||||
className="w-5 h-5 text-[#9ca3af]"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
@@ -287,10 +287,10 @@ export default function UsersPage() {
|
||||
</svg>
|
||||
)}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-[#666666]">
|
||||
{new Date(user.createdAt).toLocaleDateString()}
|
||||
</td>
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||
<td className="px-6 py-4 whitespace-nowrap text-sm text-[#666666]">
|
||||
{user.lastLoginAt
|
||||
? new Date(user.lastLoginAt).toLocaleDateString()
|
||||
: 'Never'}
|
||||
@@ -304,8 +304,8 @@ export default function UsersPage() {
|
||||
|
||||
{/* Pagination */}
|
||||
{totalPages > 1 && (
|
||||
<div className="px-6 py-4 border-t border-gray-200 flex items-center justify-between">
|
||||
<p className="text-sm text-gray-500">
|
||||
<div className="px-6 py-4 border-t border-[#e5e7eb] flex items-center justify-between">
|
||||
<p className="text-sm text-[#666666]">
|
||||
Showing {(currentPage - 1) * limit + 1} to{' '}
|
||||
{Math.min(currentPage * limit, totalUsers)} of {totalUsers} users
|
||||
</p>
|
||||
@@ -313,14 +313,14 @@ export default function UsersPage() {
|
||||
<button
|
||||
onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
|
||||
disabled={currentPage === 1}
|
||||
className="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1 border border-[#e5e7eb] rounded-md text-sm font-medium text-[#00293d] hover:bg-[#f5f9f8] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
|
||||
disabled={currentPage === totalPages}
|
||||
className="px-3 py-1 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1 border border-[#e5e7eb] rounded-md text-sm font-medium text-[#00293d] hover:bg-[#f5f9f8] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user