beautify: admin navigation

This commit is contained in:
DeathKaioken 2025-11-17 22:36:08 +01:00
parent 0b325bf44c
commit 2439928eff
2 changed files with 124 additions and 105 deletions

View File

@ -3,11 +3,13 @@
import PageLayout from '../components/PageLayout'
import {
UsersIcon,
ShieldCheckIcon,
ExclamationTriangleIcon,
CpuChipIcon,
ServerStackIcon,
ArrowRightIcon
ArrowRightIcon,
Squares2X2Icon,
BanknotesIcon,
ClipboardDocumentListIcon
} from '@heroicons/react/24/outline'
import { useMemo, useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
@ -100,118 +102,86 @@ export default function AdminDashboardPage() {
</div>
</div>
{/* Top grid: User Management + Permission Management */}
<div className="grid gap-6 lg:grid-cols-2 mb-8">
{/* User Management Card */}
{/* Management Shortcuts Card - full width */}
<div className="mb-8">
<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 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>
<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">
Manage users, view stats, and handle verification.
Quick access to common admin modules.
</p>
</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">Total Users</dt>
<dd className="font-semibold text-gray-900">
{userStats ? displayStats.totalUsers : (
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
)}
</dd>
</div>
<div>
<dt className="text-gray-500">Admin Users</dt>
<dd className="font-semibold text-gray-900">
{userStats ? displayStats.adminUsers : (
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
)}
</dd>
</div>
<div>
<dt className="text-gray-500">Verification Pending</dt>
<dd className="font-semibold text-amber-600">
{userStats ? displayStats.verificationPending : (
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
)}
</dd>
</div>
<div>
<dt className="text-gray-500">Active Users</dt>
<dd className="font-semibold text-emerald-600">
{userStats ? displayStats.activeUsers : (
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
)}
</dd>
</div>
<div>
<dt className="text-gray-500">Personal Users</dt>
<dd className="font-semibold text-gray-900">
{userStats ? displayStats.personalUsers : (
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
)}
</dd>
</div>
<div>
<dt className="text-gray-500">Company Users</dt>
<dd className="font-semibold text-gray-900">
{userStats ? displayStats.companyUsers : (
<span className="inline-block w-8 h-4 bg-gray-200 rounded animate-pulse" />
)}
</dd>
</div>
</dl>
<button
type="button"
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"
onClick={() => router.push('/admin/user-verify')}
>
Click here to Verify Users
<ArrowRightIcon className="h-4 w-4" />
</button>
</div>
{/* Permission Management Card */}
<div className="rounded-xl border border-gray-200 bg-[#f8f5ff] p-6 shadow-sm hover:shadow-md transition">
<div className="flex items-start gap-3 mb-5">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-violet-100">
<ShieldCheckIcon className="h-6 w-6 text-violet-600" />
</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 className="grid grid-cols-1 md:grid-cols-2 gap-3">
<button
type="button"
onClick={() => router.push('/admin/matrix-management')}
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"
>
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-blue-50 border border-blue-100">
<Squares2X2Icon className="h-5 w-5 text-blue-600" />
</span>
<div className="text-left">
<div className="text-sm font-semibold text-[#0d315f]">Matrix Management</div>
<div className="text-[11px] text-[#52739a]">Configure matrices and users</div>
</div>
</div>
<ArrowRightIcon className="h-4 w-4 text-blue-600 opacity-70 group-hover:opacity-100" />
</button>
<button
type="button"
onClick={() => router.push('/admin/subscriptions')}
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"
>
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-amber-50 border border-amber-100">
<BanknotesIcon className="h-5 w-5 text-amber-600" />
</span>
<div className="text-left">
<div className="text-sm font-semibold text-[#6b4e16]">Coffee Subscription Management</div>
<div className="text-[11px] text-[#8a6c2b]">Plans, billing and renewals</div>
</div>
</div>
<ArrowRightIcon className="h-4 w-4 text-amber-600 opacity-70 group-hover:opacity-100" />
</button>
<button
type="button"
onClick={() => router.push('/admin/contract-management')}
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"
>
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-indigo-50 border border-indigo-100">
<ClipboardDocumentListIcon className="h-5 w-5 text-indigo-600" />
</span>
<div className="text-left">
<div className="text-sm font-semibold text-[#1f2a5a]">Contract Management</div>
<div className="text-[11px] text-[#4b5a9a]">Templates, approvals, status</div>
</div>
</div>
<ArrowRightIcon className="h-4 w-4 text-indigo-600 opacity-70 group-hover:opacity-100" />
</button>
<button
type="button"
onClick={() => router.push('/admin/user-management')}
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"
>
<div className="flex items-center gap-3">
<span className="inline-flex h-9 w-9 items-center justify-center rounded-md bg-gray-50 border border-gray-100">
<UsersIcon className="h-5 w-5 text-blue-600" />
</span>
<div className="text-left">
<div className="text-sm font-semibold text-[#0d315f]">User Management</div>
<div className="text-[11px] text-[#52739a]">Browse, search, and manage all users</div>
</div>
</div>
<ArrowRightIcon className="h-4 w-4 text-blue-600 opacity-70 group-hover:opacity-100" />
</button>
</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>

View File

@ -124,6 +124,16 @@ export default function AdminUserManagementPage() {
const totalPages = Math.max(1, Math.ceil(filtered.length / 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
if (!isClient) {
return (
@ -243,7 +253,7 @@ export default function AdminUserManagementPage() {
<PageLayout>
<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">
{/* Title (unified with referral management) */}
{/* Title */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900">User Management</h1>
<p className="text-gray-600 mt-2">
@ -251,6 +261,45 @@ export default function AdminUserManagementPage() {
</p>
</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 && (
<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">