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
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:
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)
|
||||||
@@ -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
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
+593
-185
File diff suppressed because it is too large.
Load diff
+65
-1
@@ -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
@@ -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",
|
||||||
|
|||||||
Reference in new issue
Block a user