profit-planet-frontend/src/app/admin/page.tsx
2026-01-14 01:22:08 +01:00

405 lines
20 KiB
TypeScript

'use client'
import PageLayout from '../components/PageLayout'
import Waves from '../components/waves'
import {
UsersIcon,
ExclamationTriangleIcon,
CpuChipIcon,
ServerStackIcon,
ArrowRightIcon,
Squares2X2Icon,
BanknotesIcon,
ClipboardDocumentListIcon
} from '@heroicons/react/24/outline'
import { useMemo, useState, useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { useAdminUsers } from '../hooks/useAdminUsers'
// env-based feature flags
const DISPLAY_MATRIX = process.env.NEXT_PUBLIC_DISPLAY_MATRIX !== 'false'
const DISPLAY_ABONEMENTS = process.env.NEXT_PUBLIC_DISPLAY_ABONEMMENTS !== 'false'
const DISPLAY_NEWS = process.env.NEXT_PUBLIC_DISPLAY_NEWS !== 'false'
export default function AdminDashboardPage() {
const router = useRouter()
const { userStats, isAdmin } = useAdminUsers()
const [isClient, setIsClient] = useState(false)
// Handle client-side mounting
useEffect(() => {
setIsClient(true)
}, [])
// Fallback for loading/no data
const displayStats = userStats || {
totalUsers: 0,
adminUsers: 0,
verificationPending: 0,
activeUsers: 0,
personalUsers: 0,
companyUsers: 0
}
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 }[]
}), [])
// Show loading during SSR/initial client render
if (!isClient) {
return (
<PageLayout>
<div className="min-h-screen flex items-center justify-center bg-blue-50">
<div className="text-center">
<div className="h-12 w-12 rounded-full border-2 border-blue-900 border-b-transparent animate-spin mx-auto mb-4" />
<p className="text-blue-900">Loading...</p>
</div>
</div>
</PageLayout>
)
}
// Access check (only after client-side hydration)
if (!isAdmin) {
return (
<PageLayout>
<div className="min-h-screen flex items-center justify-center bg-blue-50">
<div className="mx-auto w-full max-w-xl rounded-2xl bg-white shadow ring-1 ring-red-500/20 p-8">
<div className="text-center">
<h1 className="text-2xl font-bold text-red-600 mb-2">Access Denied</h1>
<p className="text-gray-600">You need admin privileges to access this page.</p>
</div>
</div>
</div>
</PageLayout>
)
}
return (
<PageLayout>
<div
className="relative w-full flex flex-col min-h-screen overflow-hidden"
style={{ backgroundImage: 'none', background: 'none' }}
>
<Waves
className="pointer-events-none"
lineColor="#0f172a"
backgroundColor="rgba(245, 245, 240, 1)"
waveSpeedX={0.02}
waveSpeedY={0.01}
waveAmpX={40}
waveAmpY={20}
friction={0.9}
tension={0.01}
maxCursorMove={120}
xGap={12}
yGap={36}
/>
<div className="relative z-10 min-h-screen flex flex-col">
<main className="flex-1 max-w-7xl mx-auto px-2 sm:px-6 lg:px-8 py-8 w-full">
<div className="rounded-3xl bg-white/70 backdrop-blur-md border border-white/60 shadow-lg p-6 sm:p-8">
{/* Header */}
<header className="flex flex-col gap-4 mb-8">
<div>
<h1 className="text-4xl font-extrabold text-blue-900 tracking-tight">Admin Dashboard</h1>
<p className="text-lg text-blue-700 mt-2">
Manage all administrative features, user management, permissions, and global settings.
</p>
</div>
</header>
{/* Warning banner */}
<div className="rounded-2xl border border-red-300 bg-red-50 text-red-700 px-8 py-6 flex gap-3 items-start text-base mb-8 shadow">
<ExclamationTriangleIcon className="h-6 w-6 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>
{/* Stats Card */}
<div className="mb-8 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-6">
<div className="rounded-xl bg-white border border-gray-100 p-5 text-center shadow">
<div className="text-xs text-gray-500">Total Users</div>
<div className="text-xl font-semibold text-blue-900">{displayStats.totalUsers}</div>
</div>
<div className="rounded-xl bg-white border border-gray-100 p-5 text-center shadow">
<div className="text-xs text-gray-500">Admins</div>
<div className="text-xl font-semibold text-indigo-700">{displayStats.adminUsers}</div>
</div>
<div className="rounded-xl bg-white border border-gray-100 p-5 text-center shadow">
<div className="text-xs text-gray-500">Active</div>
<div className="text-xl font-semibold text-green-700">{displayStats.activeUsers}</div>
</div>
<div className="rounded-xl bg-white border border-gray-100 p-5 text-center shadow">
<div className="text-xs text-gray-500">Pending Verification</div>
<div className="text-xl font-semibold text-amber-700">{displayStats.verificationPending}</div>
</div>
<div className="rounded-xl bg-white border border-gray-100 p-5 text-center shadow">
<div className="text-xs text-gray-500">Personal</div>
<div className="text-xl font-semibold text-blue-700">{displayStats.personalUsers}</div>
</div>
<div className="rounded-xl bg-white border border-gray-100 p-5 text-center shadow">
<div className="text-xs text-gray-500">Company</div>
<div className="text-xl font-semibold text-purple-700">{displayStats.companyUsers}</div>
</div>
</div>
{/* Management Shortcuts Card */}
<div className="mb-8">
<div className="rounded-2xl border border-gray-100 bg-white p-8 shadow-lg hover:shadow-xl transition">
<div className="flex items-start gap-4 mb-6">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-blue-100">
<Squares2X2Icon className="h-7 w-7 text-blue-600" />
</div>
<div>
<h2 className="text-lg font-semibold text-blue-900">Management Shortcuts</h2>
<p className="text-sm text-blue-700 mt-0.5">
Quick access to common admin modules.
</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Matrix Management */}
<button
type="button"
disabled={!DISPLAY_MATRIX}
onClick={DISPLAY_MATRIX ? () => router.push('/admin/matrix-management') : undefined}
className={`group w-full flex items-center justify-between rounded-lg px-4 py-4 ${
DISPLAY_MATRIX
? 'border border-blue-200 bg-blue-50 hover:bg-blue-100 transform transition-transform duration-200 hover:scale-[1.02] hover:shadow-md'
: 'border border-gray-200 bg-gray-100 text-gray-400 cursor-not-allowed'
}`}
>
<div className="flex items-center gap-4">
<span
className={`inline-flex h-10 w-10 items-center justify-center rounded-md border ${
DISPLAY_MATRIX
? 'bg-blue-100 border-blue-200 group-hover:animate-pulse'
: 'bg-gray-100 border-gray-300'
}`}
>
<Squares2X2Icon className={`h-6 w-6 ${DISPLAY_MATRIX ? 'text-blue-600' : 'text-gray-400'}`} />
</span>
<div className="text-left">
<div className="text-base font-semibold text-blue-900">Matrix Management</div>
<div className="text-xs text-blue-700">Configure matrices and users</div>
{!DISPLAY_MATRIX && (
<p className="mt-1 text-xs text-gray-500 italic">
This module is currently disabled in the system configuration.
</p>
)}
</div>
</div>
<ArrowRightIcon
className={`h-5 w-5 ${
DISPLAY_MATRIX ? 'text-blue-600 opacity-70 group-hover:opacity-100' : 'text-gray-400 opacity-60'
}`}
/>
</button>
{/* Coffee Subscription Management */}
<button
type="button"
disabled={!DISPLAY_ABONEMENTS}
onClick={DISPLAY_ABONEMENTS ? () => router.push('/admin/subscriptions') : undefined}
className={`group w-full flex items-center justify-between rounded-lg px-4 py-4 ${
DISPLAY_ABONEMENTS
? 'border border-amber-200 bg-amber-50 hover:bg-amber-100 transform transition-transform duration-200 hover:scale-[1.02] hover:shadow-md'
: 'border border-gray-200 bg-gray-100 text-gray-400 cursor-not-allowed'
}`}
>
<div className="flex items-center gap-4">
<span
className={`inline-flex h-10 w-10 items-center justify-center rounded-md border ${
DISPLAY_ABONEMENTS
? 'bg-amber-100 border-amber-200 group-hover:animate-pulse'
: 'bg-gray-100 border-gray-300'
}`}
>
<BanknotesIcon className={`h-6 w-6 ${DISPLAY_ABONEMENTS ? 'text-amber-600' : 'text-gray-400'}`} />
</span>
<div className="text-left">
<div className="text-base font-semibold text-amber-900">Coffee Subscription Management</div>
<div className="text-xs text-amber-700">Plans, billing and renewals</div>
{!DISPLAY_ABONEMENTS && (
<p className="mt-1 text-xs text-gray-500 italic">
This module is currently disabled in the system configuration.
</p>
)}
</div>
</div>
<ArrowRightIcon
className={`h-5 w-5 ${
DISPLAY_ABONEMENTS
? 'text-amber-600 opacity-70 group-hover:opacity-100'
: 'text-gray-400 opacity-60'
}`}
/>
</button>
{/* Contract Management (unchanged) */}
<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-indigo-50 hover:bg-indigo-100 px-4 py-4 transform transition-transform duration-200 hover:scale-[1.02] hover:shadow-md"
>
<div className="flex items-center gap-4">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-md bg-indigo-100 border border-indigo-200 group-hover:animate-pulse">
<ClipboardDocumentListIcon className="h-6 w-6 text-indigo-600" />
</span>
<div className="text-left">
<div className="text-base font-semibold text-indigo-900">Contract Management</div>
<div className="text-xs text-indigo-700">Templates, approvals, status</div>
</div>
</div>
<ArrowRightIcon className="h-5 w-5 text-indigo-600 opacity-70 group-hover:opacity-100" />
</button>
{/* User Management (unchanged) */}
<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-gray-50 hover:bg-blue-50 px-4 py-4 transform transition-transform duration-200 hover:scale-[1.02] hover:shadow-md"
>
<div className="flex items-center gap-4">
<span className="inline-flex h-10 w-10 items-center justify-center rounded-md bg-blue-100 border border-blue-200 group-hover:animate-pulse">
<UsersIcon className="h-6 w-6 text-blue-600" />
</span>
<div className="text-left">
<div className="text-base font-semibold text-blue-900">User Management</div>
<div className="text-xs text-blue-700">Browse, search, and manage all users</div>
</div>
</div>
<ArrowRightIcon className="h-5 w-5 text-blue-600 opacity-70 group-hover:opacity-100" />
</button>
{/* News Management */}
<button
type="button"
disabled={!DISPLAY_NEWS}
onClick={DISPLAY_NEWS ? () => router.push('/admin/news-management') : undefined}
className={`group w-full flex items-center justify-between rounded-lg px-4 py-4 ${
DISPLAY_NEWS
? 'border border-green-200 bg-green-50 hover:bg-green-100 transform transition-transform duration-200 hover:scale-[1.02] hover:shadow-md'
: 'border border-gray-200 bg-gray-100 text-gray-400 cursor-not-allowed'
}`}
>
<div className="flex items-center gap-4">
<span
className={`inline-flex h-10 w-10 items-center justify-center rounded-md border ${
DISPLAY_NEWS
? 'bg-green-100 border-green-200 group-hover:animate-pulse'
: 'bg-gray-100 border-gray-300'
}`}
>
<ClipboardDocumentListIcon className={`h-6 w-6 ${DISPLAY_NEWS ? 'text-green-600' : 'text-gray-400'}`} />
</span>
<div className="text-left">
<div className="text-base font-semibold text-green-900">News Management</div>
<div className="text-xs text-green-700">Create and manage news articles</div>
{!DISPLAY_NEWS && (
<p className="mt-1 text-xs text-gray-500 italic">
This module is currently disabled in the system configuration.
</p>
)}
</div>
</div>
<ArrowRightIcon
className={`h-5 w-5 ${
DISPLAY_NEWS ? 'text-green-600 opacity-70 group-hover:opacity-100' : 'text-gray-400 opacity-60'
}`}
/>
</button>
</div>
</div>
</div>
{/* Server Status & Logs */}
<div className="rounded-2xl border border-gray-100 bg-white p-8 shadow-lg hover:shadow-xl transition">
<div className="flex items-start gap-4 mb-6">
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-gray-100">
<ServerStackIcon className="h-7 w-7 text-gray-700" />
</div>
<div>
<h2 className="text-lg font-semibold text-gray-900">
Server Status & Logs
</h2>
<p className="text-sm text-gray-500 mt-0.5">
System health, resource usage & recent error insights.
</p>
</div>
</div>
<div className="grid gap-8 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-base">
<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-sm 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-sm 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-base font-semibold text-gray-800 mb-3">
Recent Error Logs
</h3>
{serverStats.recentErrors.length === 0 && (
<p className="text-sm 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-lg border border-gray-300 bg-gray-50 hover:bg-gray-100 text-gray-700 text-sm font-medium px-4 py-3 transition"
// TODO: navigate to logs / monitoring page
onClick={() => {}}
>
View Full Logs
<ArrowRightIcon className="h-5 w-5" />
</button>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</PageLayout>
)
}