diff --git a/src/app/admin/commandocentrum/page.tsx b/src/app/admin/commandocentrum/page.tsx index 22c1c87f..122df50f 100644 --- a/src/app/admin/commandocentrum/page.tsx +++ b/src/app/admin/commandocentrum/page.tsx @@ -16,7 +16,7 @@ import { executeCommand, sendGift, } from "@/actions/commandocentrum"; -import { DiagnosticRow, InfoItem, StatusCard } from "@/components/admin/dashboard"; +import { DiagnosticRow, InfoItem, StatusCard, OnlineUsersWidget } from "@/components/admin/dashboard"; import { prisma } from "@/lib/prisma"; import { rcon } from "@/lib/services/rcon"; import { siteSettings } from "@/lib/services/site-settings"; @@ -122,6 +122,15 @@ export default async function CommandoCentrum() { /> + {/* ── Online users widget ───────────────────────────────── */} +
+

Online Users

+ +
+ {/* ── Server info + diagnostics ──────────────────────────── */}
diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index 03603647..172d4df8 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -70,9 +70,9 @@ const NAV_GROUPS: { label: string; items: { href: string; label: string }[] }[] { href: "/admin/maintenance", label: "Maintenance" }, { href: "/admin/housekeeping", label: "Housekeeping" }, { href: "/admin/rooms", label: "Rooms" }, - { href: "/admin/email-templates", label: "Email" }, - { href: "/admin/theme", label: "Theme" }, - { href: "/admin/media", label: "Media" }, + { href: "/admin/email-templates", label: "Email templates" }, + { href: "/admin/theme", label: "Theme" }, + { href: "/admin/media", label: "Media" }, { href: "/admin/settings", label: "Settings" }, ], }, diff --git a/src/components/admin/dashboard.tsx b/src/components/admin/dashboard.tsx index aeeed261..9e85114b 100644 --- a/src/components/admin/dashboard.tsx +++ b/src/components/admin/dashboard.tsx @@ -1,5 +1,14 @@ import type { ReactNode } from "react"; +export interface OnlineUser { + id: number; + username: string; + look: string; + roomId: number | null; + roomName: string | null; + lastActivity: Date; +} + type State = "ok" | "warn" | "danger" | "neutral"; /** A single live-status tile (online users, emulator, DB, …). */ @@ -64,3 +73,206 @@ export function InfoItem({ label, value }: { label: string; value: ReactNode })
); } + +/** Live list of online users with room and activity tracking */ +export function OnlineUsersWidget({ + users, + isLoading = false, +}: { + users: OnlineUser[]; + isLoading?: boolean; +}) { + const formatLastSeen = (date: Date) => { + const now = new Date(); + const diff = Math.floor((now.getTime() - date.getTime()) / 1000); + + if (diff < 60) return "Just now"; + if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; + return `${Math.floor(diff / 86400)}d ago`; + }; + + return ( +
+
+

Online Now

+ + {isLoading ? "..." : users.length} users + +
+ +
+ {users.length === 0 ? ( +
+ No users online +
+ ) : ( + users.map((user) => ( +
+
+ {user.username} + {user.roomId && user.roomName && ( +
+ +
+ )} +
+ +
+
+ + {user.username} + +
+
+ {user.roomId && user.roomName ? ( + + 🏠 {user.roomName} + + ) : ( + ⏱️ {formatLastSeen(user.lastActivity)} + )} +
+
+
+ )) + )} +
+ + +
+ ); +}