import Link from "next/link"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { applyPreset, bulkImportPermissions, clearAllPermissions, deletePermission, testRankPermission, upsertPermission, } from "@/actions/admin-housekeeping"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { HousekeepingClient } from "./client"; export const dynamic = "force-dynamic"; const GROUPS = [ "Core", "Users", "Rooms", "Bans", "News", "Logs", "Settings", "Wordfilter", "Catalog", "Shop", "Events", "Tickets", "Moderation", "Polls", "Analytics", "DevOps", "Prefixes", "Banners", "Radio", "RCON", "Assets", "Export", "Notifications", "CMS Pages", ]; export default async function AdminHousekeeping({ searchParams, }: { searchParams: Promise<{ q?: string; mode?: string; tab?: | "overview" | "manage" | "import" | "ranks" | "presets" | "test" | "audit"; group?: string; }>; }) { const ctx = await getAdminContext(); if (!canAccess(ctx.permissions, PERMS.SETTINGS_VIEW, ctx.session.user.rank)) { redirect("/admin"); } const t = await getTranslations("pages.admin.housekeeping"); const sp = await searchParams; const q = (sp.q ?? "").trim(); const tab = sp.tab ?? "overview"; const groupFilter = (sp.group ?? "").trim(); type HKPerm = Awaited< ReturnType >[0]; const allPermissions: HKPerm[] = await prisma.websiteHousekeepingPermissions .findMany({ orderBy: [{ groupName: "asc" }, { permission: "asc" }] }) .catch(() => []); const filtered = allPermissions.filter((p) => { if (q && !p.permission.toLowerCase().includes(q.toLowerCase())) return false; if (groupFilter && (p.groupName ?? "") !== groupFilter) return false; return true; }); const ranks = allPermissions.map((p) => p.minRank); const maxRank = ranks.length ? Math.max(...ranks) : 0; const totalMin = ranks.reduce((sum, r) => sum + r, 0); const groupCounts: Record = {}; for (const p of allPermissions) { const g: string = p.groupName ?? "Uncategorized"; groupCounts[g] = (groupCounts[g] || 0) + 1; } const rankGroups: Record = {}; for (const p of allPermissions) { if (!rankGroups[p.minRank]) rankGroups[p.minRank] = []; rankGroups[p.minRank].push(p); } const missingDeps: Array<{ permission: string; dependsOn: string }> = []; const permSet = new Set(allPermissions.map((p) => p.permission)); for (const p of allPermissions) { if (p.dependsOn && !permSet.has(p.dependsOn)) { missingDeps.push({ permission: p.permission, dependsOn: p.dependsOn }); } } return (
{t("tabOverview")} {t("tabManage")} {t("tabImport")} {t("tabRanks")} {t("tabPresets")} {t("tabTest")} {t("tabAudit")}
{tab === "overview" && ( <>
{t("permissionsCount", { count: allPermissions.length })} {t("highestMinRank", { rank: maxRank })} {t("totalMinRank", { total: totalMin })} {t("groupCount", { count: Object.keys(groupCounts).length })} {t("rankCount", { count: Object.keys(rankGroups).length })} {missingDeps.length > 0 && ( {t("missingDeps", { count: missingDeps.length })} )}

{t("perGroup")}

{Object.entries(groupCounts) .sort(([a], [b]) => a.localeCompare(b)) .map(([group, count]) => (
{group}
{count} {count === 1 ? t("permission") : t("permissions")}
))}

{t("perRank")}

{Object.entries(rankGroups) .sort(([a], [b]) => Number(b) - Number(a)) .map(([rank, perms]) => ( ))}
{t("rank")} {t("count")} {t("permissions")}
Rank {rank} {perms.length} {perms.map((p) => p.permission).join(", ")}
{missingDeps.length > 0 && (

{t("missingDeps")}

    {missingDeps.map((d) => (
  • {d.permission} {t("dependsOn")}{" "} {d.dependsOn} — {t("notFound")}
  • ))}
)} )} {tab === "manage" && ( <>

{t("addPermission")}

{t("permissions")}{" "} {filtered.length}

{filtered.length === 0 ? (
{t("noPermissions")}
) : (
{filtered.map((p, i) => ( ))}
# {t("colPermission")} {t("colDescription")} {t("colGroup")} {t("colMinRank")} {t("colDepends")}
{i + 1} {p.permission} {p.description ?? "—"} {p.groupName ? ( {p.groupName} ) : ( — )}
{p.dependsOn ? ( {p.dependsOn} {!permSet.has(p.dependsOn) ? " " + t("missing") : ""} ) : ( — )}
)}
)} {tab === "import" && ( <>

{t("exportTitle")}

{t("exportHint")}

{t("importTitle")}

{t("importHint")}