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 ───────────────────────────────── */}
+
+
{/* ── 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.roomId && user.roomName && (
+
+
+
+ )}
+
+
+
+
+
+ {user.roomId && user.roomName ? (
+
+ 🏠 {user.roomName}
+
+ ) : (
+ ⏱️ {formatLastSeen(user.lastActivity)}
+ )}
+
+
+
+ ))
+ )}
+
+
+
+
+ );
+}