feat(mod): tickets+team tabs; retire HK writes for live ACL
Deploy / release (push) Skipped
Deploy / deploy (push) Failing after 30s

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
SimoandCursor committed 2026-07-22 21:35:35 +02:00
1 parent 084cca6ea4
commit 75cace41ea
16 files changed
+735 -1002

No files matched your search

+49 -3
View File
@@ -1,4 +1,4 @@
import { Ban, Gavel, Radio, Shield } from "lucide-react";
import { Ban, Gavel, Radio, Shield, Ticket, Users } from "lucide-react";
import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { StatsCard } from "@/components/admin/stats-card";
@@ -23,8 +23,14 @@ export default async function ModDashboardPage() {
const showBans =
canAccess(permissions, PERMS.MOD_BANS_VIEW, rank) ||
canAccess(permissions, PERMS.BANS_VIEW, rank);
const showTickets =
canAccess(permissions, PERMS.MOD_TICKETS_VIEW, rank) ||
canAccess(permissions, PERMS.TICKETS_VIEW, rank);
const showTeam =
canAccess(permissions, PERMS.MOD_TEAM_VIEW, rank) ||
canAccess(permissions, PERMS.MODERATION_VIEW, rank);
const [openCfh, todayBans, modsOnline] = await Promise.all([
const [openCfh, todayBans, modsOnline, openTickets] = await Promise.all([
showCfh
? prisma.supportTickets.count({ where: { state: 0 } }).catch(() => 0)
: Promise.resolve(0),
@@ -36,6 +42,11 @@ export default async function ModDashboardPage() {
prisma.user
.count({ where: { online: "1", rank: { gte: minStaffRank } } })
.catch(() => 0),
showTickets
? prisma.websiteTicket
.count({ where: { status: { not: "closed" } } })
.catch(() => 0)
: Promise.resolve(0),
]);
return (
@@ -47,7 +58,7 @@ export default async function ModDashboardPage() {
</p>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{showCfh ? (
<StatsCard
title={t("stats.openCfh")}
@@ -56,6 +67,14 @@ export default async function ModDashboardPage() {
href="/mod/cfh"
/>
) : null}
{showTickets ? (
<StatsCard
title={t("stats.openTickets")}
value={openTickets}
icon={<Ticket className="h-4 w-4" />}
href="/mod/tickets"
/>
) : null}
{showBans ? (
<StatsCard
title={t("stats.bansToday")}
@@ -68,6 +87,7 @@ export default async function ModDashboardPage() {
title={t("stats.modsOnline")}
value={modsOnline}
icon={<Shield className="h-4 w-4" />}
href={showTeam ? "/mod/team" : undefined}
/>
{showActions ? (
<StatsCard
@@ -78,6 +98,15 @@ export default async function ModDashboardPage() {
href="/mod/actions"
/>
) : null}
{showTeam ? (
<StatsCard
title={t("stats.team")}
value="→"
description={t("stats.teamHint")}
icon={<Users className="h-4 w-4" />}
href="/mod/team"
/>
) : null}
</div>
<section className="admin-card space-y-2 p-4">
@@ -90,6 +119,16 @@ export default async function ModDashboardPage() {
</Link>
</li>
) : null}
{showTickets ? (
<li>
<Link
href="/mod/tickets"
className="text-primary hover:underline"
>
{t("nav.tickets")}
</Link>
</li>
) : null}
{showActions ? (
<li>
<Link
@@ -107,6 +146,13 @@ export default async function ModDashboardPage() {
</Link>
</li>
) : null}
{showTeam ? (
<li>
<Link href="/mod/team" className="text-primary hover:underline">
{t("nav.team")}
</Link>
</li>
) : null}
</ul>
</section>
</div>