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 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>

View File

@ -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">