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

+14 -5
View File
@@ -5,16 +5,20 @@ import { prisma } from "@/lib/prisma";
import { fetchEmulatorRankSummaries } from "@/lib/services/permission-ranks";
import { PermissionsList } from "./permissions-list";
export default async function PermissionsPage(_props: {
export default async function PermissionsPage({
searchParams,
}: {
params: Promise<{ locale: string }>;
searchParams?: Promise<{ from?: string }>;
}) {
const { session, permissions } = await getAdminContext();
if (
!canAccess(permissions, PERMS.PERMISSIONS_MANAGE, session.user.rank)
) {
if (!canAccess(permissions, PERMS.PERMISSIONS_MANAGE, session.user.rank)) {
redirect("/admin");
}
const sp = (await searchParams) ?? {};
const fromHousekeeping = sp.from === "housekeeping";
const ranks = await fetchEmulatorRankSummaries(prisma);
// User counts per rank
@@ -31,5 +35,10 @@ export default async function PermissionsPage(_props: {
userCount: countMap[rank.id] ?? 0,
}));
return <PermissionsList ranks={ranksWithCounts} />;
return (
<PermissionsList
ranks={ranksWithCounts}
fromHousekeeping={fromHousekeeping}
/>
);
}
+16 -3
View File
@@ -42,6 +42,7 @@ interface RankSummary {
interface Props {
ranks: RankSummary[];
fromHousekeeping?: boolean;
}
const RANK_ICONS: Record<number, React.ReactNode> = {
@@ -58,7 +59,10 @@ const RANK_COLORS: Record<number, string> = {
4: "border-[var(--admin-success-border)] bg-[var(--admin-success-subtle)]",
};
export function PermissionsList({ ranks: initialRanks }: Props) {
export function PermissionsList({
ranks: initialRanks,
fromHousekeeping = false,
}: Props) {
const { run, isPending } = useServerAction();
const [ranks, setRanks] = useState(initialRanks);
const [deleteTarget, setDeleteTarget] = useState<RankSummary | null>(null);
@@ -98,14 +102,23 @@ export function PermissionsList({ ranks: initialRanks }: Props) {
return (
<div className="space-y-6">
{fromHousekeeping ? (
<div
className="rounded-lg border border-[var(--admin-warning-border)] bg-[var(--admin-warning-subtle)] px-4 py-3 text-sm"
role="status"
>
Housekeeping writes are retired. Edit live ACL ranks and permissions
here.
</div>
) : null}
<div
className="rounded-lg border border-[var(--admin-info-border)] bg-[var(--admin-info-subtle)] px-4 py-3 text-sm text-[var(--admin-text)]"
role="status"
>
<p className="m-0 font-semibold">Live ACL</p>
<p className="m-0 mt-1 text-muted-foreground">
Rank ACL here controls real admin access. The Housekeeping table is
legacy reference only and does not gate routes.
Rank ACL here controls real admin and mod access. The Housekeeping
table is a read-only legacy archive with an ACL compare view.
</p>
</div>