beautify: admin navigation
This commit is contained in:
parent
0b325bf44c
commit
2439928eff
@ -3,11 +3,13 @@
|
|||||||
import PageLayout from '../components/PageLayout'
|
import PageLayout from '../components/PageLayout'
|
||||||
import {
|
import {
|
||||||
UsersIcon,
|
UsersIcon,
|
||||||
ShieldCheckIcon,
|
|
||||||
ExclamationTriangleIcon,
|
ExclamationTriangleIcon,
|
||||||
CpuChipIcon,
|
CpuChipIcon,
|
||||||
ServerStackIcon,
|
ServerStackIcon,
|
||||||
ArrowRightIcon
|
ArrowRightIcon,
|
||||||
|
Squares2X2Icon,
|
||||||
|
BanknotesIcon,
|
||||||
|
ClipboardDocumentListIcon
|
||||||
} from '@heroicons/react/24/outline'
|
} from '@heroicons/react/24/outline'
|
||||||
import { useMemo, useState, useEffect } from 'react'
|
import { useMemo, useState, useEffect } from 'react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
@ -100,118 +102,86 @@ export default function AdminDashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Top grid: User Management + Permission Management */}
|
{/* Management Shortcuts Card - full width */}
|
||||||
<div className="grid gap-6 lg:grid-cols-2 mb-8">
|
<div className="mb-8">
|
||||||
{/* User Management Card */}
|
|
||||||
<div className="rounded-xl border border-gray-200 bg-[#f5f9ff] p-6 shadow-sm hover:shadow-md transition">
|
<div className="rounded-xl border border-gray-200 bg-[#f5f9ff] p-6 shadow-sm hover:shadow-md transition">
|
||||||
<div className="flex items-start gap-3 mb-5">
|
<div className="flex items-start gap-3 mb-5">
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100">
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-100">
|
||||||
<UsersIcon className="h-6 w-6 text-blue-600" />
|
<Squares2X2Icon className="h-6 w-6 text-blue-600" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-sm font-semibold text-[#0d315f]">User Management</h2>
|
<h2 className="text-sm font-semibold text-[#0d315f]">Management Shortcuts</h2>
|
||||||
<p className="text-xs text-[#52739a] mt-0.5">
|
<p className="text-xs text-[#52739a] mt-0.5">
|
||||||
Manage users, view stats, and handle verification.
|
Quick access to common admin modules.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<dl className="grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-3 text-xs sm:text-sm mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
<div>
|
<button
|
||||||
<dt className="text-gray-500">Total Users</dt>
|
type="button"
|
||||||
<dd className="font-semibold text-gray-900">
|
onClick={() => router.push('/admin/matrix-management')}
|
||||||
{userStats ? displayStats.totalUsers : (
|
className="group w-full flex items-center justify-between rounded-lg border border-blue-200 bg-white/70 hover:bg-white px-3 py-3 transition"
|
||||||
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
|
>
|
||||||
)}
|
<div className="flex items-center gap-3">
|
||||||
</dd>
|
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-blue-50 border border-blue-100">
|
||||||
</div>
|
<Squares2X2Icon className="h-5 w-5 text-blue-600" />
|
||||||
<div>
|
</span>
|
||||||
<dt className="text-gray-500">Admin Users</dt>
|
<div className="text-left">
|
||||||
<dd className="font-semibold text-gray-900">
|
<div className="text-sm font-semibold text-[#0d315f]">Matrix Management</div>
|
||||||
{userStats ? displayStats.adminUsers : (
|
<div className="text-[11px] text-[#52739a]">Configure matrices and users</div>
|
||||||
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
|
</div>
|
||||||
)}
|
</div>
|
||||||
</dd>
|
<ArrowRightIcon className="h-4 w-4 text-blue-600 opacity-70 group-hover:opacity-100" />
|
||||||
</div>
|
</button>
|
||||||
<div>
|
<button
|
||||||
<dt className="text-gray-500">Verification Pending</dt>
|
type="button"
|
||||||
<dd className="font-semibold text-amber-600">
|
onClick={() => router.push('/admin/subscriptions')}
|
||||||
{userStats ? displayStats.verificationPending : (
|
className="group w-full flex items-center justify-between rounded-lg border border-amber-200 bg-white/70 hover:bg-white px-3 py-3 transition"
|
||||||
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
|
>
|
||||||
)}
|
<div className="flex items-center gap-3">
|
||||||
</dd>
|
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-amber-50 border border-amber-100">
|
||||||
</div>
|
<BanknotesIcon className="h-5 w-5 text-amber-600" />
|
||||||
<div>
|
</span>
|
||||||
<dt className="text-gray-500">Active Users</dt>
|
<div className="text-left">
|
||||||
<dd className="font-semibold text-emerald-600">
|
<div className="text-sm font-semibold text-[#6b4e16]">Coffee Subscription Management</div>
|
||||||
{userStats ? displayStats.activeUsers : (
|
<div className="text-[11px] text-[#8a6c2b]">Plans, billing and renewals</div>
|
||||||
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
|
</div>
|
||||||
)}
|
</div>
|
||||||
</dd>
|
<ArrowRightIcon className="h-4 w-4 text-amber-600 opacity-70 group-hover:opacity-100" />
|
||||||
</div>
|
</button>
|
||||||
<div>
|
<button
|
||||||
<dt className="text-gray-500">Personal Users</dt>
|
type="button"
|
||||||
<dd className="font-semibold text-gray-900">
|
onClick={() => router.push('/admin/contract-management')}
|
||||||
{userStats ? displayStats.personalUsers : (
|
className="group w-full flex items-center justify-between rounded-lg border border-indigo-200 bg-white/70 hover:bg-white px-3 py-3 transition"
|
||||||
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
|
>
|
||||||
)}
|
<div className="flex items-center gap-3">
|
||||||
</dd>
|
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-indigo-50 border border-indigo-100">
|
||||||
</div>
|
<ClipboardDocumentListIcon className="h-5 w-5 text-indigo-600" />
|
||||||
<div>
|
</span>
|
||||||
<dt className="text-gray-500">Company Users</dt>
|
<div className="text-left">
|
||||||
<dd className="font-semibold text-gray-900">
|
<div className="text-sm font-semibold text-[#1f2a5a]">Contract Management</div>
|
||||||
{userStats ? displayStats.companyUsers : (
|
<div className="text-[11px] text-[#4b5a9a]">Templates, approvals, status</div>
|
||||||
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
|
</div>
|
||||||
)}
|
</div>
|
||||||
</dd>
|
<ArrowRightIcon className="h-4 w-4 text-indigo-600 opacity-70 group-hover:opacity-100" />
|
||||||
</div>
|
</button>
|
||||||
</dl>
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
onClick={() => router.push('/admin/user-management')}
|
||||||
className="w-full inline-flex items-center justify-center gap-2 rounded-md bg-amber-50 hover:bg-amber-100 border border-amber-200 text-amber-700 text-xs sm:text-sm font-medium px-4 py-2.5 transition"
|
className="group w-full flex items-center justify-between rounded-lg border border-gray-200 bg-white/70 hover:bg-white px-3 py-3 transition"
|
||||||
onClick={() => router.push('/admin/user-verify')}
|
>
|
||||||
>
|
<div className="flex items-center gap-3">
|
||||||
Click here to Verify Users
|
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-gray-50 border border-gray-100">
|
||||||
<ArrowRightIcon className="h-4 w-4" />
|
<UsersIcon className="h-5 w-5 text-blue-600" />
|
||||||
</button>
|
</span>
|
||||||
</div>
|
<div className="text-left">
|
||||||
|
<div className="text-sm font-semibold text-[#0d315f]">User Management</div>
|
||||||
{/* Permission Management Card */}
|
<div className="text-[11px] text-[#52739a]">Browse, search, and manage all users</div>
|
||||||
<div className="rounded-xl border border-gray-200 bg-[#f8f5ff] p-6 shadow-sm hover:shadow-md transition">
|
</div>
|
||||||
<div className="flex items-start gap-3 mb-5">
|
</div>
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-violet-100">
|
<ArrowRightIcon className="h-4 w-4 text-blue-600 opacity-70 group-hover:opacity-100" />
|
||||||
<ShieldCheckIcon className="h-6 w-6 text-violet-600" />
|
</button>
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 className="text-sm font-semibold text-[#3f2c6e]">
|
|
||||||
Permission Management
|
|
||||||
</h2>
|
|
||||||
<p className="text-xs text-[#705ca1] mt-0.5">
|
|
||||||
Set roles and permissions for users.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<dl className="grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-3 text-xs sm:text-sm mb-6">
|
|
||||||
<div>
|
|
||||||
<dt className="text-gray-500">Permissions</dt>
|
|
||||||
<dd className="font-semibold text-gray-900">{permissionStats.permissions}</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-gray-500">Role Templates</dt>
|
|
||||||
<dd className="font-semibold text-gray-900">—</dd>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<dt className="text-gray-500">Revoked</dt>
|
|
||||||
<dd className="font-semibold text-gray-900">0</dd>
|
|
||||||
</div>
|
|
||||||
</dl>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="w-full inline-flex items-center justify-center gap-2 rounded-md bg-violet-50 hover:bg-violet-100 border border-violet-200 text-violet-700 text-xs sm:text-sm font-medium px-4 py-2.5 transition"
|
|
||||||
onClick={() => router.push('/admin/user-management')}
|
|
||||||
>
|
|
||||||
Go to Permission Management
|
|
||||||
<ArrowRightIcon className="h-4 w-4" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -124,6 +124,16 @@ export default function AdminUserManagementPage() {
|
|||||||
const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE))
|
const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE))
|
||||||
const current = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)
|
const current = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE)
|
||||||
|
|
||||||
|
// Move stats calculation above all conditional returns to avoid hook order errors
|
||||||
|
const stats = useMemo(() => ({
|
||||||
|
total: allUsers.length,
|
||||||
|
admins: allUsers.filter(u => u.role === 'admin').length,
|
||||||
|
personal: allUsers.filter(u => u.user_type === 'personal').length,
|
||||||
|
company: allUsers.filter(u => u.user_type === 'company').length,
|
||||||
|
active: allUsers.filter(u => u.is_admin_verified === 1).length,
|
||||||
|
pending: allUsers.filter(u => u.is_admin_verified !== 1).length,
|
||||||
|
}), [allUsers])
|
||||||
|
|
||||||
// Show loading during SSR/initial client render
|
// Show loading during SSR/initial client render
|
||||||
if (!isClient) {
|
if (!isClient) {
|
||||||
return (
|
return (
|
||||||
@ -243,7 +253,7 @@ export default function AdminUserManagementPage() {
|
|||||||
<PageLayout>
|
<PageLayout>
|
||||||
<div className="min-h-screen bg-gray-50">
|
<div className="min-h-screen bg-gray-50">
|
||||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
{/* Title (unified with referral management) */}
|
{/* Title */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h1 className="text-3xl font-bold text-gray-900">User Management</h1>
|
<h1 className="text-3xl font-bold text-gray-900">User Management</h1>
|
||||||
<p className="text-gray-600 mt-2">
|
<p className="text-gray-600 mt-2">
|
||||||
@ -251,6 +261,45 @@ export default function AdminUserManagementPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Statistic Section + Verify Button */}
|
||||||
|
<div className="mb-8 flex flex-col sm:flex-row sm:items-center gap-4">
|
||||||
|
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-4 flex-1">
|
||||||
|
<div className="rounded-lg bg-white border border-gray-200 p-3 text-center">
|
||||||
|
<div className="text-[11px] text-gray-500">Total Users</div>
|
||||||
|
<div className="text-base font-semibold text-gray-900">{stats.total}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg bg-white border border-gray-200 p-3 text-center">
|
||||||
|
<div className="text-[11px] text-gray-500">Admins</div>
|
||||||
|
<div className="text-base font-semibold text-indigo-700">{stats.admins}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg bg-white border border-gray-200 p-3 text-center">
|
||||||
|
<div className="text-[11px] text-gray-500">Personal</div>
|
||||||
|
<div className="text-base font-semibold text-blue-700">{stats.personal}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg bg-white border border-gray-200 p-3 text-center">
|
||||||
|
<div className="text-[11px] text-gray-500">Company</div>
|
||||||
|
<div className="text-base font-semibold text-purple-700">{stats.company}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg bg-white border border-gray-200 p-3 text-center">
|
||||||
|
<div className="text-[11px] text-gray-500">Active</div>
|
||||||
|
<div className="text-base font-semibold text-green-700">{stats.active}</div>
|
||||||
|
</div>
|
||||||
|
<div className="rounded-lg bg-white border border-gray-200 p-3 text-center">
|
||||||
|
<div className="text-[11px] text-gray-500">Pending</div>
|
||||||
|
<div className="text-base font-semibold text-amber-700">{stats.pending}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center gap-2 rounded-md bg-amber-50 hover:bg-amber-100 border border-amber-200 text-amber-700 text-sm font-medium px-4 py-2.5 transition"
|
||||||
|
onClick={() => window.location.href = '/admin/user-verify'}
|
||||||
|
>
|
||||||
|
Go to User Verification
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Error Message */}
|
{/* Error Message */}
|
||||||
{error && (
|
{error && (
|
||||||
<div className="rounded-lg border border-red-300 bg-red-50 text-red-700 px-5 py-4 flex gap-3 items-start mb-6">
|
<div className="rounded-lg border border-red-300 bg-red-50 text-red-700 px-5 py-4 flex gap-3 items-start mb-6">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user