import { MessageSquare, Shield, Terminal, Users } from "lucide-react"; import { redirect } from "next/navigation"; import { StatsCard } from "@/components/admin/stats-card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; async function getActivityData() { const now = Math.floor(Date.now() / 1000); const todayStart = now - (now % 86400); const weekAgo = now - 7 * 86400; // Daily activity breakdown — 4 GROUP BY queries instead of 28 sequential queries const rangeStart = todayStart - 6 * 86400; const [chatRows, cmdRows, banRows, regRows] = await Promise.all([ prisma.$queryRaw<{ day_bucket: bigint; count: bigint }[]>` SELECT FLOOR(timestamp / 86400) AS day_bucket, COUNT(*) AS count FROM chatlogs_room WHERE timestamp >= ${rangeStart} GROUP BY day_bucket`, prisma.$queryRaw<{ day_bucket: bigint; count: bigint }[]>` SELECT FLOOR(timestamp / 86400) AS day_bucket, COUNT(*) AS count FROM commandlogs WHERE timestamp >= ${rangeStart} GROUP BY day_bucket`, prisma.$queryRaw<{ day_bucket: bigint; count: bigint }[]>` SELECT FLOOR(timestamp / 86400) AS day_bucket, COUNT(*) AS count FROM bans WHERE timestamp >= ${rangeStart} GROUP BY day_bucket`, prisma.$queryRaw<{ day_bucket: bigint; count: bigint }[]>` SELECT FLOOR(account_created / 86400) AS day_bucket, COUNT(*) AS count FROM users WHERE account_created >= ${rangeStart} GROUP BY day_bucket`, ]); const toMap = (rows: { day_bucket: bigint; count: bigint }[]) => new Map(rows.map((r) => [Number(r.day_bucket), Number(r.count)])); const chatMap = toMap(chatRows); const cmdMap = toMap(cmdRows); const banMap = toMap(banRows); const regMap = toMap(regRows); const dailyData = []; for (let i = 6; i >= 0; i--) { const dayStart = todayStart - i * 86400; const bucket = Math.floor(dayStart / 86400); const date = new Date(dayStart * 1000); dailyData.push({ label: date.toLocaleDateString("en", { weekday: "short", month: "short", day: "numeric" }), chats: chatMap.get(bucket) ?? 0, commands: cmdMap.get(bucket) ?? 0, bans: banMap.get(bucket) ?? 0, registrations: regMap.get(bucket) ?? 0, }); } // Totals const [weekChatRows, weekCommandRows, weekBans, weekRegistrations] = await Promise.all([ prisma.$queryRaw< { count: bigint }[] >`SELECT COUNT(*) AS count FROM chatlogs_room WHERE timestamp >= ${weekAgo}`, prisma.$queryRaw< { count: bigint }[] >`SELECT COUNT(*) AS count FROM commandlogs WHERE timestamp >= ${weekAgo}`, prisma.ban.count({ where: { timestamp: { gte: weekAgo } } }), prisma.user.count({ where: { accountCreated: { gte: weekAgo } } }), ]); const weekChats = Number(weekChatRows[0]?.count ?? 0); const weekCommands = Number(weekCommandRows[0]?.count ?? 0); // Most active users (by chat messages) const topChatters = await prisma.$queryRaw<{ userId: number; messages: bigint }[]>` SELECT user_from_id AS userId, COUNT(*) AS messages FROM chatlogs_room WHERE timestamp >= ${weekAgo} GROUP BY user_from_id ORDER BY messages DESC LIMIT 10 `; const userIds = topChatters.map((c) => c.userId); const users = await prisma.user.findMany({ where: { id: { in: userIds } }, select: { id: true, username: true }, }); const userMap = new Map(users.map((u) => [u.id, u.username])); const topChattersWithNames = topChatters.map((c) => ({ userId: c.userId, username: userMap.get(c.userId) ?? `User #${c.userId}`, messages: Number(c.messages), })); return { dailyData, weekChats, weekCommands, weekBans, weekRegistrations, topChatters: topChattersWithNames, }; } export default async function ActivityPage() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.ANALYTICS_VIEW, session.user.rank)) { redirect("/admin"); } const data = await getActivityData(); return (

Activity

User activity breakdown and engagement metrics

} /> } /> } /> } />
{/* Daily breakdown table */} Daily Breakdown (7 days)
{data.dailyData.map((day) => ( ))}
Day Chats Cmds Bans Regs
{day.label} {day.chats.toLocaleString()} {day.commands.toLocaleString()} {day.bans} {day.registrations}
{/* Top chatters */} Most Active Users (7 days) {data.topChatters.length === 0 ? (

No chat activity

) : (
{data.topChatters.map((user, i) => (
{i + 1} {user.username} {user.messages.toLocaleString()} msgs
))}
)}
); }