import { and, asc, count, desc, eq, like, or, type SQL } from "drizzle-orm"; import Link from "next/link"; import { getTranslations } from "next-intl/server"; import { DataTable } from "@/components/admin/data-table"; import { Badge } from "@/components/ui/badge"; import { requireModPermission } from "@/lib/admin/guard"; import { calcPagination, parseListParams } from "@/lib/admin-helpers"; import { db, User } from "@/lib/db"; import { getAvatarUrl } from "@/lib/imager"; import { PERMS } from "@/lib/permissions"; import type { DataTableColumn } from "@/types/common"; type ModUserRow = { id: number; username: string; rank: number; online: string; motto: string; look: string; lastLogin: number | null; }; const columns: DataTableColumn[] = [ { key: "id", label: "ID", sortable: true }, { key: "username", label: "User", sortable: true, render: (_, row) => (
{row.username}
), }, { key: "rank", label: "Rank", sortable: true, render: (v) => #{String(v)}, }, { key: "online", label: "Online", render: (v) => v === "1" ? ( Online ) : ( Offline ), }, { key: "motto", label: "Motto", render: (v) => ( {String(v || "—")} ), }, { key: "actions", label: "", render: (_, row) => (
Mod view Public
), }, ]; const SORT_COLS = { id: User.id, username: User.username, rank: User.rank, online: User.online, motto: User.motto, lastLogin: User.lastLogin, } as const; export default async function ModUsersPage({ searchParams, }: { searchParams: Promise>; }) { await requireModPermission([PERMS.MOD_USERS_VIEW, PERMS.USERS_VIEW]); const t = await getTranslations("pages.mod"); const raw = await searchParams; const parsed = parseListParams(new URLSearchParams(raw)); const conditions: SQL[] = []; if (parsed.search.trim()) { const q = parsed.search.trim(); const ors = [like(User.username, `%${q}%`), like(User.motto, `%${q}%`)]; if (Number.isFinite(Number(q)) && Number(q) > 0) { ors.push(eq(User.id, Number(q))); } const searchFilter = or(...ors); if (searchFilter) conditions.push(searchFilter); } const where = conditions.length > 0 ? and(...conditions) : undefined; const sortKey = parsed.sort && parsed.sort in SORT_COLS ? (parsed.sort as keyof typeof SORT_COLS) : "id"; const sortCol = SORT_COLS[sortKey]; const finalOrder = parsed.sort && parsed.sort in SORT_COLS ? parsed.order === "asc" ? asc(sortCol) : desc(sortCol) : desc(User.id); const [totals, users] = await Promise.all([ db .select({ total: count() }) .from(User) .where(where) .then((rows) => rows[0]?.total ?? 0) .catch(() => 0), db .select({ id: User.id, username: User.username, rank: User.rank, online: User.online, motto: User.motto, look: User.look, lastLogin: User.lastLogin, }) .from(User) .where(where) .orderBy(finalOrder) .offset((parsed.page - 1) * parsed.perPage) .limit(parsed.perPage) .catch(() => [] as ModUserRow[]), ]); const total = totals; const pagination = calcPagination(total, parsed.page, parsed.perPage); const rows: ModUserRow[] = users; return (

{t("usersTitle")}

{t("usersSubtitle")}

{total === 0 && !parsed.search ? (
{t("usersEmpty")}
) : ( )}
); }