feature: add admin dashboard

This commit is contained in:
DeathKaioken 2025-10-04 00:45:38 +02:00
parent 10d3d341bc
commit b2cfb1aa34

253
src/app/admin/page.tsx Normal file
View 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>
)
}