feat(admin): extend housekeeping permissions page with groups, presets, audit, test, import/export, rank overview and dependency tracking
Local Build and Deploy / deploy (push) Successful in 1m14s

- Add group_name, depends_on and updated_by columns to website_housekeeping_permissions
- Add migration 0016 for new columns
- Rewrite housekeeping page with 7 tabs: Overview, Manage, Import/Export, Rank overview, Presets, Test, Audit log
- Add permission groups/categories with filtering
- Add bulk JSON import/export
- Add rank overview grouped by permission category
- Add permission presets for Moderator (rank 5), Admin (rank 7), Super Admin (rank 8)
- Add audit logging for all permission changes via AdminAuditLog
- Add test mode to check permissions by rank
- Add dependency tracking with warnings for missing dependencies
- Add overview dashboard with statistics
- Add Dutch and English translations for all new features
This commit is contained in:
openhands committed 2026-07-17 19:02:23 +02:00
1 parent 49d204e85c
commit 98d4b715df
7 files changed
+1636 -220

No files matched your search

@@ -0,0 +1,6 @@
ALTER TABLE `website_housekeeping_permissions`
ADD COLUMN `group_name` VARCHAR(100) DEFAULT NULL AFTER `description`,
ADD COLUMN `depends_on` VARCHAR(255) DEFAULT NULL AFTER `group_name`,
ADD COLUMN `updated_by` INT DEFAULT NULL AFTER `updated_at`;
-- Migration tracking (no model change needed for audit log usage)
+3
View File
@@ -1623,6 +1623,9 @@ model WebsiteHousekeepingPermissions {
permission String @unique @db.VarChar(255) permission String @unique @db.VarChar(255)
minRank Int @map("min_rank") minRank Int @map("min_rank")
description String? @db.VarChar(255) description String? @db.VarChar(255)
groupName String? @map("group_name") @db.VarChar(100)
dependsOn String? @map("depends_on") @db.VarChar(255)
updatedBy Int? @map("updated_by")
createdAt DateTime? @map("created_at") @db.Timestamp(0) createdAt DateTime? @map("created_at") @db.Timestamp(0)
updatedAt DateTime? @map("updated_at") @db.Timestamp(0) updatedAt DateTime? @map("updated_at") @db.Timestamp(0)
File diff suppressed because it is too large. Load diff
+53
View File
@@ -0,0 +1,53 @@
"use client";
import { useTranslations } from "next-intl";
import { useCallback, useState } from "react";
export function HousekeepingClient() {
const t = useTranslations("pages.admin.housekeeping");
const [exportData, setExportData] = useState<string | null>(null);
const [copying, setCopying] = useState(false);
const handleExport = useCallback(async () => {
const { exportPermissions } = await import("@/actions/admin-housekeeping");
const data = await exportPermissions();
setExportData(data);
}, []);
const handleCopy = useCallback(async () => {
if (!exportData) return;
setCopying(true);
try {
await navigator.clipboard.writeText(exportData);
} catch {
// fallback
}
setTimeout(() => setCopying(false), 1500);
}, [exportData]);
return (
<div>
<button
type="button"
onClick={handleExport}
className="btn btn-outline mb-3"
>
{t("export")}
</button>
{exportData && (
<div className="relative">
<pre className="admin-card font-mono text-xs overflow-auto max-h-[300px] p-3">
{exportData}
</pre>
<button
type="button"
onClick={handleCopy}
className="btn btn-outline btn-sm absolute top-2 right-2"
>
{copying ? t("copied") : t("copy")}
</button>
</div>
)}
</div>
);
}
File diff suppressed because it is too large. Load diff
+65 -1
View File
@@ -1879,7 +1879,71 @@
"save": "Save permission", "save": "Save permission",
"saving": "Saving…", "saving": "Saving…",
"cancel": "Cancel" "cancel": "Cancel"
} },
"confirmClearAll": "Delete ALL permissions? This action cannot be undone!",
"colGroup": "Group",
"colDepends": "Depends on",
"colPermission": "Permission",
"colDescription": "Description",
"colMinRank": "Min. Rank",
"permissionKey": "Permission key",
"minRank": "Min. rank",
"description": "Description",
"permissions": "Permissions",
"permissionsCount": "{count} permissions",
"permission": "permission",
"highestMinRank": "Highest min. rank: {rank}",
"totalMinRank": "Total min. rank sum: {total}",
"groupCount": "{count} groups",
"rankCount": "{count} ranks",
"missingDeps": "{count} missing dependencies",
"dependsOn": "depends on",
"notFound": "not found",
"missing": "(missing)",
"rank": "Rank",
"count": "Count",
"searchPermission": "Search permissions…",
"search": "Search",
"manage": "Manage",
"importSelection": "Import selection",
"noGroup": "No group",
"allGroups": "All groups",
"perGroup": "Permissions per group",
"perRank": "Permissions per rank",
"rankOverviewHint": "All permissions grouped by the rank they are assigned to.",
"tabOverview": "Overview",
"tabManage": "Manage",
"tabImport": "Import / Export",
"tabRanks": "Rank overview",
"tabPresets": "Presets",
"tabTest": "Test",
"tabAudit": "Audit log",
"importPermissions": "Import permissions",
"importHint": "Paste a JSON array of permission objects. Format: [{ \"permission\": \"...\", \"minRank\": 7, \"description\": \"...\", \"groupName\": \"...\", \"dependsOn\": \"...\" }]",
"importPlaceholder": "Paste JSON here…",
"import": "Import",
"exportTitle": "Export permissions",
"exportHint": "Export all permissions as JSON for backup or batch editing.",
"export": "Export JSON",
"copy": "Copy",
"copied": "Copied!",
"presets": "Permission presets",
"presetsHint": "Apply a predefined set of permissions for common staff roles.",
"presetModerator": "Moderator",
"presetModeratorDesc": "Basic moderation tools: user lookup, bans, CFH handling, chat logs.",
"presetAdmin": "Administrator",
"presetAdminDesc": "Full admin access: users, rooms, news, settings, shop, events, radio & more.",
"presetSuperAdmin": "Super Admin",
"presetSuperAdminDesc": "Complete access to every feature including permission management and asset import.",
"applyPreset": "Apply preset",
"clearAll": "Clear all permissions",
"testTitle": "Test rank access",
"testHint": "Enter a rank number to see which permissions would be granted.",
"test": "Test",
"auditTitle": "Permission audit log",
"auditHint": "All changes to housekeeping permissions are logged.",
"auditLogsAt": "Audit logs are available on the Audit log page.",
"viewAuditLogs": "View audit logs"
}, },
"rooms": { "rooms": {
"title": "Rooms", "title": "Rooms",
+65 -1
View File
@@ -1879,7 +1879,71 @@
"save": "Permissie opslaan", "save": "Permissie opslaan",
"saving": "Opslaan…", "saving": "Opslaan…",
"cancel": "Annuleren" "cancel": "Annuleren"
} },
"confirmClearAll": "ALLE permissies verwijderen? Dit kan niet ongedaan worden gemaakt!",
"colGroup": "Groep",
"colDepends": "Afhankelijk van",
"colPermission": "Permissie",
"colDescription": "Omschrijving",
"colMinRank": "Min. rang",
"permissionKey": "Permissiesleutel",
"minRank": "Min. rang",
"description": "Omschrijving",
"permissions": "Permissies",
"permissionsCount": "{count} permissies",
"permission": "permissie",
"highestMinRank": "Hoogste min. rang: {rank}",
"totalMinRank": "Totale min. rang som: {total}",
"groupCount": "{count} groepen",
"rankCount": "{count} rangen",
"missingDeps": "{count} ontbrekende afhankelijkheden",
"dependsOn": "afhankelijk van",
"notFound": "niet gevonden",
"missing": "(ontbreekt)",
"rank": "Rang",
"count": "Aantal",
"searchPermission": "Zoek permissies…",
"search": "Zoeken",
"manage": "Beheer",
"importSelection": "Importeer selectie",
"noGroup": "Geen groep",
"allGroups": "Alle groepen",
"perGroup": "Permissies per groep",
"perRank": "Permissies per rang",
"rankOverviewHint": "Alle permissies gegroepeerd per rang.",
"tabOverview": "Overzicht",
"tabManage": "Beheer",
"tabImport": "Importeren / Exporteren",
"tabRanks": "Rangenoverzicht",
"tabPresets": "Presets",
"tabTest": "Test",
"tabAudit": "Auditlog",
"importPermissions": "Permissies importeren",
"importHint": "Plak een JSON-array met permissie-objecten. Formaat: [{ \"permission\": \"...\", \"minRank\": 7, \"description\": \"...\", \"groupName\": \"...\", \"dependsOn\": \"...\" }]",
"importPlaceholder": "Plak JSON hier…",
"import": "Importeren",
"exportTitle": "Permissies exporteren",
"exportHint": "Exporteer alle permissies als JSON voor backup of bulkbewerking.",
"export": "Exporteer JSON",
"copy": "Kopiëren",
"copied": "Gekopieerd!",
"presets": "Permissie-presets",
"presetsHint": "Pas een vooraf gedefinieerde set permissies toe voor veelvoorkomende staffrollen.",
"presetModerator": "Moderator",
"presetModeratorDesc": "Basis moderatie tools: gebruikers zoeken, bans, CFH afhandeling, chatlogs.",
"presetAdmin": "Administrator",
"presetAdminDesc": "Volledige admin-toegang: gebruikers, kamers, nieuws, instellingen, winkel, events, radio & meer.",
"presetSuperAdmin": "Super Admin",
"presetSuperAdminDesc": "Volledige toegang tot alle functies inclusief permissiebeheer en asset-import.",
"applyPreset": "Pas preset toe",
"clearAll": "Verwijder alle permissies",
"testTitle": "Test rangtoegang",
"testHint": "Voer een rangnummer in om te zien welke permissies zouden worden toegekend.",
"test": "Test",
"auditTitle": "Permissie auditlog",
"auditHint": "Alle wijzigingen aan housekeeping permissies worden gelogd.",
"auditLogsAt": "Auditlogs zijn beschikbaar op de Auditlog-pagina.",
"viewAuditLogs": "Bekijk auditlogs"
}, },
"rooms": { "rooms": {
"title": "Kamers", "title": "Kamers",