feat(admin): extend housekeeping permissions page with groups, presets, audit, test, import/export, rank overview and dependency tracking

- 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

+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>
);
}