Introduces new admin dashboard pages for alerts, bans, invites, orders, settings, status, and vendors under app/dashboard/admin/. Moves the main admin page to the new dashboard structure and adds a shared admin layout. Updates sidebar configuration and adds supporting components and hooks for admin features.
211 lines
8.1 KiB
TypeScript
211 lines
8.1 KiB
TypeScript
import React from "react";
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Server, Database, Cpu, HardDrive, Activity } from "lucide-react";
|
|
import { fetchServer } from "@/lib/api";
|
|
|
|
interface SystemStatus {
|
|
uptimeSeconds: number;
|
|
memory: {
|
|
rss: number;
|
|
heapTotal: number;
|
|
heapUsed: number;
|
|
external: number;
|
|
arrayBuffers: number;
|
|
};
|
|
versions: Record<string, string>;
|
|
counts: {
|
|
vendors: number;
|
|
orders: number;
|
|
products: number;
|
|
chats: number;
|
|
};
|
|
}
|
|
|
|
export default async function AdminStatusPage() {
|
|
let systemStatus: SystemStatus | null = null;
|
|
let error: string | null = null;
|
|
|
|
try {
|
|
systemStatus = await fetchServer<SystemStatus>("/admin/system-status");
|
|
} catch (err) {
|
|
console.error("Failed to fetch system status:", err);
|
|
error = "Failed to load system status";
|
|
}
|
|
if (error) {
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h1 className="text-2xl font-semibold tracking-tight">System Status</h1>
|
|
<p className="text-sm text-muted-foreground mt-1">Monitor system health and performance metrics</p>
|
|
</div>
|
|
<Card>
|
|
<CardContent className="pt-6">
|
|
<div className="text-center text-red-500">
|
|
<p>{error}</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const formatUptime = (seconds: number) => {
|
|
const days = Math.floor(seconds / 86400);
|
|
const hours = Math.floor((seconds % 86400) / 3600);
|
|
const minutes = Math.floor((seconds % 3600) / 60);
|
|
return `${days}d ${hours}h ${minutes}m`;
|
|
};
|
|
|
|
const formatBytes = (bytes: number) => {
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
if (bytes === 0) return '0 Bytes';
|
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i];
|
|
};
|
|
|
|
const memoryUsagePercent = systemStatus ?
|
|
Math.round((systemStatus.memory.heapUsed / systemStatus.memory.heapTotal) * 100) : 0;
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div>
|
|
<h1 className="text-2xl font-semibold tracking-tight">System Status</h1>
|
|
<p className="text-sm text-muted-foreground mt-1">Monitor system health and performance metrics</p>
|
|
</div>
|
|
|
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
|
|
{/* Server Status */}
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Server Status</CardTitle>
|
|
<Server className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge variant="default" className="bg-green-500">Online</Badge>
|
|
<span className="text-sm text-muted-foreground">
|
|
{systemStatus ? formatUptime(systemStatus.uptimeSeconds) : 'N/A'}
|
|
</span>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-2">
|
|
Last checked: {new Date().toLocaleTimeString()}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Database Status */}
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Database</CardTitle>
|
|
<Database className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge variant="default" className="bg-green-500">Connected</Badge>
|
|
<span className="text-sm text-muted-foreground">
|
|
{systemStatus ? `${systemStatus.counts.vendors + systemStatus.counts.orders + systemStatus.counts.products} records` : 'N/A'}
|
|
</span>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-2">
|
|
Total collections: 4
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Memory Usage */}
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Memory</CardTitle>
|
|
<HardDrive className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge variant={memoryUsagePercent > 80 ? "destructive" : memoryUsagePercent > 60 ? "secondary" : "outline"}>
|
|
{memoryUsagePercent}%
|
|
</Badge>
|
|
<span className="text-sm text-muted-foreground">
|
|
{systemStatus ? formatBytes(systemStatus.memory.heapUsed) : 'N/A'}
|
|
</span>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-2">
|
|
Total: {systemStatus ? formatBytes(systemStatus.memory.heapTotal) : 'N/A'}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Platform Stats */}
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Platform Stats</CardTitle>
|
|
<Activity className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge variant="default" className="bg-green-500">Active</Badge>
|
|
<span className="text-sm text-muted-foreground">
|
|
{systemStatus ? `${systemStatus.counts.vendors} vendors` : 'N/A'}
|
|
</span>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-2">
|
|
{systemStatus ? `${systemStatus.counts.orders} orders, ${systemStatus.counts.products} products` : 'N/A'}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Node.js Version */}
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">Runtime</CardTitle>
|
|
<Activity className="h-4 w-4 text-muted-foreground" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center space-x-2">
|
|
<Badge variant="outline">
|
|
{systemStatus ? `Node ${systemStatus.versions.node}` : 'N/A'}
|
|
</Badge>
|
|
<span className="text-sm text-muted-foreground">Runtime</span>
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-2">
|
|
{systemStatus ? `V8: ${systemStatus.versions.v8}` : 'N/A'}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Recent Activity */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Recent System Activity</CardTitle>
|
|
<CardDescription>Latest system events and changes</CardDescription>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center space-x-4">
|
|
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
|
<div className="flex-1">
|
|
<p className="text-sm font-medium">System health check completed</p>
|
|
<p className="text-xs text-muted-foreground">2 minutes ago</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
|
<div className="flex-1">
|
|
<p className="text-sm font-medium">Database backup completed</p>
|
|
<p className="text-xs text-muted-foreground">1 hour ago</p>
|
|
</div>
|
|
</div>
|
|
<div className="flex items-center space-x-4">
|
|
<div className="w-2 h-2 bg-yellow-500 rounded-full"></div>
|
|
<div className="flex-1">
|
|
<p className="text-sm font-medium">High memory usage detected</p>
|
|
<p className="text-xs text-muted-foreground">3 hours ago</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|