import { and, count, desc, gte, inArray, like } from "drizzle-orm"; import Link from "next/link"; import { redirect } from "next/navigation"; import { AvatarImage } from "@/components/shared/avatar-image"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import { getMinStaffRank } from "@/lib/admin/min-staff-rank"; import { AdminAuditLog, db, SupportTickets, User, WebsiteTicket, } from "@/lib/db"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; // TODO: Cache Components adoption. Refactor this route so this opt-out can be removed. // See: https://nextjs.org/docs/app/guides/migrating-to-cache-components export const instant = false; export default async function ModTeamPage() { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.MODERATION_VIEW, session.user.rank)) redirect("/admin"); const minStaffRank = await getMinStaffRank(); const staff = await db .select({ id: User.id, username: User.username, look: User.look, rank: User.rank, online: User.online, lastLogin: User.lastLogin, lastOnline: User.lastOnline, }) .from(User) .where(gte(User.rank, minStaffRank)) .orderBy(desc(User.online), desc(User.rank)); const staffIds = staff.map((s) => s.id); // Get CFH ticket counts per mod and web ticket counts const [cfhCounts, webTicketCounts, modActionCounts] = await Promise.all([ staffIds.length ? db .select({ modId: SupportTickets.modId, total: count(), }) .from(SupportTickets) .where(inArray(SupportTickets.modId, staffIds)) .groupBy(SupportTickets.modId) : Promise.resolve([]), staffIds.length ? db .select({ assigneeId: WebsiteTicket.assigneeId, total: count(), }) .from(WebsiteTicket) .where(inArray(WebsiteTicket.assigneeId, staffIds)) .groupBy(WebsiteTicket.assigneeId) : Promise.resolve([]), staffIds.length ? db .select({ userId: AdminAuditLog.userId, total: count(), }) .from(AdminAuditLog) .where( and( inArray(AdminAuditLog.userId, staffIds), like(AdminAuditLog.action, "mod_%"), ), ) .groupBy(AdminAuditLog.userId) : Promise.resolve([]), ]); const cfhMap = new Map(cfhCounts.map((c) => [c.modId, Number(c.total)])); const webMap = new Map( webTicketCounts.map((c) => [c.assigneeId, Number(c.total)]), ); const actionMap = new Map( modActionCounts.map((c) => [c.userId, Number(c.total)]), ); return (
{/* Summary */}

{staff.filter((s) => s.online === "1").length}

Online Now

{staff.length}

Total Staff

{staff.filter((s) => s.rank > minStaffRank).length}

Above min rank

{/* Staff list */}
Name Rank CFH Tickets Actions Last Seen
{staff.map((member) => (

{member.username}

Rank {member.rank}

{cfhMap.get(member.id) ?? 0}

CFH

{webMap.get(member.id) ?? 0}

Web

{actionMap.get(member.id) ?? 0}

Mod

{member.online === "1" ? "Online" : member.lastOnline ? new Date(member.lastOnline * 1000).toLocaleDateString() : "—"} ))}
); }