feature: add admin dashboard
This commit is contained in:
parent
10d3d341bc
commit
b2cfb1aa34
253
src/app/admin/page.tsx
Normal file
253
src/app/admin/page.tsx
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import PageLayout from '../components/PageLayout'
|
||||||
|
import {
|
||||||
|
UsersIcon,
|
||||||
|
ShieldCheckIcon,
|
||||||
|
ExclamationTriangleIcon,
|
||||||
|
CpuChipIcon,
|
||||||
|
ServerStackIcon,
|
||||||
|
ArrowRightIcon
|
||||||
|
} from '@heroicons/react/24/outline'
|
||||||
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
|
export default function AdminDashboardPage() {
|
||||||
|
// Mocked aggregates (replace with real fetch)
|
||||||
|
const userStats = useMemo(() => ({
|
||||||
|
total: 101,
|
||||||
|
admins: 1,
|
||||||
|
pending: 3,
|
||||||
|
active: 54,
|
||||||
|
personal: 94,
|
||||||
|
company: 7
|
||||||
|
}), [])
|
||||||
|
|
||||||
|
const permissionStats = useMemo(() => ({
|
||||||
|
permissions: 1 // TODO: fetch permission definitions
|
||||||
|
}), [])
|
||||||
|
|
||||||
|
const serverStats = useMemo(() => ({
|
||||||
|
status: 'Online',
|
||||||
|
uptime: '4 days, 8 hours',
|
||||||
|
cpu: '0%',
|
||||||
|
memory: '0.1 / 7.8',
|
||||||
|
recentErrors: [] as { id: string; ts: string; msg: string }[]
|
||||||
|
}), [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageLayout>
|
||||||
|
<div className="relative flex flex-col flex-1 px-4 sm:px-6 lg:px-10 py-10">
|
||||||
|
{/* Background */}
|
||||||
|
<div className="fixed inset-0 -z-10">
|
||||||
|
<div className="absolute inset-0 -z-20 bg-gradient-to-br from-[#0b3e98] via-[#0d51c2] to-[#1d66d9]" />
|
||||||
|
<svg aria-hidden="true" className="absolute inset-0 -z-10 h-full w-full stroke-white/10">
|
||||||
|
<defs>
|
||||||
|
<pattern id="admin-dash-pattern" x="50%" y={-1} width={200} height={200} patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M.5 200V.5H200" fill="none" stroke="rgba(255,255,255,0.05)" />
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect fill="url(#admin-dash-pattern)" width="100%" height="100%" strokeWidth={0} />
|
||||||
|
</svg>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
className="absolute top-0 right-0 left-1/2 -ml-24 blur-3xl transform-gpu overflow-hidden lg:ml-24 xl:ml-48"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="aspect-[801/1036] w-[50.0625rem] bg-gradient-to-tr from-[#94b3ff] to-[#a78bfa] opacity-40"
|
||||||
|
style={{ clipPath: 'polygon(63.1% 29.5%,100% 17.1%,76.6% 3%,48.4% 0%,44.6% 4.7%,54.5% 25.3%,59.8% 49%,55.2% 57.8%,44.4% 57.2%,27.8% 47.9%,35.1% 81.5%,0% 97.7%,39.2% 100%,35.2% 81.4%,97.2% 52.8%,63.1% 29.5%)' }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Outer container card */}
|
||||||
|
<div className="relative mx-auto w-full max-w-7xl bg-white/90 backdrop-blur rounded-2xl shadow-2xl ring-1 ring-black/10 p-6 sm:p-10 space-y-8">
|
||||||
|
{/* Heading */}
|
||||||
|
<div className="space-y-2">
|
||||||
|
<h1 className="text-3xl sm:text-4xl font-bold tracking-tight text-[#11306b]">
|
||||||
|
Admin Dashboard
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm sm:text-base text-[#34507d] font-medium">
|
||||||
|
Manage all administrative features, user management, permissions, and global settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Warning banner */}
|
||||||
|
<div className="rounded-lg border border-red-300 bg-red-50/90 text-red-700 px-5 py-4 flex gap-3 items-start text-sm">
|
||||||
|
<ExclamationTriangleIcon className="h-5 w-5 flex-shrink-0 text-red-500 mt-0.5" />
|
||||||
|
<div className="leading-relaxed">
|
||||||
|
<p className="font-semibold mb-0.5">
|
||||||
|
Warning: Settings and actions below this point can have consequences for the entire system!
|
||||||
|
</p>
|
||||||
|
<p className="text-red-600/80 hidden sm:block">
|
||||||
|
Manage all administrative features, user management, permissions, and global settings.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Top grid: User Management + Permission Management */}
|
||||||
|
<div className="grid gap-6 lg:grid-cols-2">
|
||||||
|
{/* User Management Card */}
|
||||||
|
<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" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-sm font-semibold text-[#0d315f]">User Management</h2>
|
||||||
|
<p className="text-xs text-[#52739a] mt-0.5">
|
||||||
|
Manage users, view stats, and handle verification.
|
||||||
|
</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.total}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-gray-500">Admin Users</dt>
|
||||||
|
<dd className="font-semibold text-gray-900">{userStats.admins}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-gray-500">Verification Pending</dt>
|
||||||
|
<dd className="font-semibold text-amber-600">{userStats.pending}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-gray-500">Active Users</dt>
|
||||||
|
<dd className="font-semibold text-emerald-600">{userStats.active}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-gray-500">Personal Users</dt>
|
||||||
|
<dd className="font-semibold text-gray-900">{userStats.personal}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt className="text-gray-500">Company Users</dt>
|
||||||
|
<dd className="font-semibold text-gray-900">{userStats.company}</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"
|
||||||
|
// TODO: onClick navigate to verification queue
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
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>
|
||||||
|
<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"
|
||||||
|
// TODO: navigate to permissions page
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
Go to Permission Management
|
||||||
|
<ArrowRightIcon className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Server Status & Logs */}
|
||||||
|
<div className="rounded-xl border border-gray-200 bg-white p-6 shadow-sm hover:shadow-md transition">
|
||||||
|
<div className="flex items-start gap-3 mb-6">
|
||||||
|
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-gray-100">
|
||||||
|
<ServerStackIcon className="h-6 w-6 text-gray-700" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-sm font-semibold text-gray-900">
|
||||||
|
Server Status & Logs
|
||||||
|
</h2>
|
||||||
|
<p className="text-xs text-gray-500 mt-0.5">
|
||||||
|
System health, resource usage & recent error insights.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-6 lg:grid-cols-3">
|
||||||
|
{/* Metrics */}
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className={`h-2.5 w-2.5 rounded-full ${serverStats.status === 'Online' ? 'bg-emerald-500' : 'bg-red-500'}`} />
|
||||||
|
<p className="text-sm">
|
||||||
|
<span className="font-semibold">Server Status:</span>{' '}
|
||||||
|
<span className={serverStats.status === 'Online' ? 'text-emerald-600 font-medium' : 'text-red-600 font-medium'}>
|
||||||
|
{serverStats.status === 'Online' ? 'Server Online' : 'Offline'}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-xs space-y-1 text-gray-600">
|
||||||
|
<p><span className="font-medium text-gray-700">Uptime:</span> {serverStats.uptime}</p>
|
||||||
|
<p><span className="font-medium text-gray-700">CPU Usage:</span> {serverStats.cpu}</p>
|
||||||
|
<p><span className="font-medium text-gray-700">Memory Usage:</span> {serverStats.memory} GB</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2 text-xs text-gray-500">
|
||||||
|
<CpuChipIcon className="h-4 w-4" />
|
||||||
|
<span>Autoscaled environment (mock)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
<div className="hidden lg:block border-l border-gray-200" />
|
||||||
|
|
||||||
|
{/* Logs */}
|
||||||
|
<div className="lg:col-span-2">
|
||||||
|
<h3 className="text-sm font-semibold text-gray-800 mb-3">
|
||||||
|
Recent Error Logs
|
||||||
|
</h3>
|
||||||
|
{serverStats.recentErrors.length === 0 && (
|
||||||
|
<p className="text-xs text-gray-500 italic">
|
||||||
|
No recent logs.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{/* Placeholder for future logs list */}
|
||||||
|
{/* TODO: Replace with mapped log entries */}
|
||||||
|
<div className="mt-6">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="inline-flex items-center gap-1.5 rounded-md border border-gray-300 bg-gray-50 hover:bg-gray-100 text-gray-700 text-xs font-medium px-3 py-2 transition"
|
||||||
|
// TODO: navigate to logs / monitoring page
|
||||||
|
onClick={() => {}}
|
||||||
|
>
|
||||||
|
View Full Logs
|
||||||
|
<ArrowRightIcon className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</PageLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user