| // src/components/admin/AdminOverview.tsx | |
| import { useState, useEffect } from "react"; | |
| import { Activity, Users, FileText, MessageSquare, Shield, Clock, Cpu, Server, Layers } from "lucide-react"; | |
| import api from "@/lib/api"; | |
| interface AdminOverviewProps { | |
| stats: any; | |
| activities: any[]; | |
| loading: boolean; | |
| } | |
| export function AdminOverview({ activities, loading }: AdminOverviewProps) { | |
| const [sysInfo, setSysInfo] = useState<any>(null); | |
| const [sysLoading, setSysLoading] = useState(true); | |
| useEffect(() => { | |
| const fetchSysInfo = async () => { | |
| try { | |
| const res = await api.get("/admin/system/info"); | |
| setSysInfo(res.data); | |
| } catch (err) { | |
| console.error("Failed to fetch system info", err); | |
| } finally { | |
| setSysLoading(false); | |
| } | |
| }; | |
| fetchSysInfo(); | |
| const interval = setInterval(fetchSysInfo, 20000); | |
| return () => clearInterval(interval); | |
| }, []); | |
| if (loading) { | |
| return ( | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| <div className="h-80 bg-white/40 dark:bg-gray-900/40 border dark:border-gray-800 rounded-2xl animate-pulse" /> | |
| <div className="h-80 bg-white/40 dark:bg-gray-900/40 border dark:border-gray-800 rounded-2xl animate-pulse" /> | |
| </div> | |
| ); | |
| } | |
| const getEntityIcon = (type?: string) => { | |
| const norm = type?.toLowerCase() || ""; | |
| if (norm === "user" || norm.includes("register")) { | |
| return <Users className="w-4 h-4 text-blue-500" />; | |
| } | |
| if (norm === "article" || norm.includes("publish")) { | |
| return <FileText className="w-4 h-4 text-emerald-500" />; | |
| } | |
| return <MessageSquare className="w-4 h-4 text-indigo-500" />; | |
| }; | |
| return ( | |
| <div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> | |
| {/* Recent Activity Timeline */} | |
| <div className="bg-white/60 dark:bg-gray-900/60 backdrop-blur-md border border-gray-200/50 dark:border-gray-800/50 rounded-2xl p-6 shadow-xl relative overflow-hidden"> | |
| <div className="absolute top-0 right-0 w-24 h-24 bg-primary/5 rounded-full blur-2xl" /> | |
| <h2 className="text-lg font-semibold flex items-center gap-2 mb-4"> | |
| <Activity className="w-5 h-5 text-primary" /> | |
| Audit / Recent Activity | |
| </h2> | |
| <div className="relative border-l border-gray-200 dark:border-gray-800 ml-3.5 space-y-5"> | |
| {activities && | |
| activities.slice(0, 5).map((activity: any, i: number) => { | |
| const entityType = activity.entityType || activity.type || ""; | |
| return ( | |
| <div key={i} className="relative pl-6"> | |
| {/* Timeline node */} | |
| <div className="absolute -left-3.5 top-0.5 w-7 h-7 rounded-full bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-850 flex items-center justify-center shadow-md"> | |
| {getEntityIcon(entityType)} | |
| </div> | |
| <div> | |
| <p className="text-sm font-medium text-gray-800 dark:text-gray-200"> | |
| {activity.description} | |
| </p> | |
| <span className="text-[10px] text-gray-400 font-semibold flex items-center gap-1 mt-0.5"> | |
| <Clock className="w-3 h-3" /> | |
| {new Date(activity.timestamp).toLocaleString()} | |
| </span> | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| {(!activities || activities.length === 0) && ( | |
| <div className="text-center text-gray-500 py-8 border-l border-transparent ml-[-14px]"> | |
| No recent administrative actions | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| {/* Live System & JVM Info */} | |
| <div className="bg-white/60 dark:bg-gray-900/60 backdrop-blur-md border border-gray-200/50 dark:border-gray-800/50 rounded-2xl p-6 shadow-xl relative overflow-hidden flex flex-col justify-between"> | |
| <div className="absolute bottom-0 right-0 w-24 h-24 bg-indigo-500/5 rounded-full blur-2xl" /> | |
| <div> | |
| <h2 className="text-lg font-semibold flex items-center gap-2 mb-1"> | |
| <Cpu className="w-5 h-5 text-violet-500" /> | |
| System Properties | |
| </h2> | |
| <p className="text-xs text-gray-500 dark:text-gray-400 mb-4">JVM environment & OS statistics</p> | |
| </div> | |
| {sysLoading ? ( | |
| <div className="flex-1 flex items-center justify-center py-8"> | |
| <div className="w-6 h-6 border-2 border-primary border-t-transparent rounded-full animate-spin" /> | |
| </div> | |
| ) : sysInfo ? ( | |
| <div className="space-y-2.5 flex-1 justify-center flex flex-col"> | |
| <div className="flex justify-between items-center py-2 border-b border-gray-100 dark:border-gray-800/60 text-sm"> | |
| <span className="text-gray-500 font-medium flex items-center gap-1.5"><Layers className="w-4 h-4" /> Java Version</span> | |
| <span className="font-semibold text-gray-700 dark:text-gray-300">{sysInfo.javaVersion || "17"}</span> | |
| </div> | |
| <div className="flex justify-between items-center py-2 border-b border-gray-100 dark:border-gray-800/60 text-sm"> | |
| <span className="text-gray-500 font-medium flex items-center gap-1.5"><Server className="w-4 h-4" /> OS Platform</span> | |
| <span className="font-semibold text-gray-700 dark:text-gray-300 truncate max-w-[200px]" title={sysInfo.osName}>{sysInfo.osName || "Linux"}</span> | |
| </div> | |
| <div className="flex justify-between items-center py-2 border-b border-gray-100 dark:border-gray-800/60 text-sm"> | |
| <span className="text-gray-500 font-medium flex items-center gap-1.5"><Cpu className="w-4 h-4" /> CPU Cores</span> | |
| <span className="font-semibold text-gray-700 dark:text-gray-300">{sysInfo.availableProcessors || "8"}</span> | |
| </div> | |
| <div className="flex justify-between items-center py-2 text-sm"> | |
| <span className="text-gray-500 font-medium flex items-center gap-1.5"><Shield className="w-4 h-4" /> Engine Uptime</span> | |
| <span className="font-semibold text-gray-750 dark:text-gray-250 bg-gray-100 dark:bg-gray-800/70 px-2 py-0.5 rounded text-xs">{sysInfo.uptime || "N/A"}</span> | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className="flex-1 flex items-center justify-center text-gray-400 text-sm"> | |
| Failed to load server environment details | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| ); | |
| } | |
Xet Storage Details
- Size:
- 6.56 kB
- Xet hash:
- f77a9c60a8f749a6b36a45ed29e73e6d78a2ce207e59cd76bd40563d855ccc0d
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.