feat(admin): reorganize housekeeping and strengthen shared workflows
This commit is contained in:
1 parent
35f66d879f
commit
a070004e7b
35 files changed
+1667
-462
No files matched your search
@@ -0,0 +1,180 @@
|
||||
import {
|
||||
Activity,
|
||||
Cog,
|
||||
LayoutDashboard,
|
||||
Newspaper,
|
||||
Package,
|
||||
ShoppingCart,
|
||||
Users,
|
||||
UsersRound,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
ADMIN_NAV_GROUPS,
|
||||
type AdminNavGroup,
|
||||
navItemIsAllowed,
|
||||
} from "@/lib/admin-nav";
|
||||
import {
|
||||
type AdminNavConfig,
|
||||
applyAdminNavConfig,
|
||||
} from "@/lib/admin-nav-config";
|
||||
|
||||
export type AdminNavigationOptions = {
|
||||
allowedPermissions: string[];
|
||||
isSuperAdmin: boolean;
|
||||
navConfig: AdminNavConfig;
|
||||
};
|
||||
const sections = [
|
||||
{ labelKey: "overview", icon: LayoutDashboard, roots: [""] },
|
||||
{
|
||||
labelKey: "peopleSupport",
|
||||
icon: Users,
|
||||
roots: [
|
||||
"users",
|
||||
"online",
|
||||
"tickets",
|
||||
"help-tickets",
|
||||
"bans",
|
||||
"ip",
|
||||
"vpn",
|
||||
"wordfilter",
|
||||
"moderation",
|
||||
"rooms",
|
||||
],
|
||||
},
|
||||
{
|
||||
labelKey: "catalogFurniture",
|
||||
icon: Package,
|
||||
roots: [
|
||||
"studio",
|
||||
"catalog",
|
||||
"import",
|
||||
"items",
|
||||
"rare-values",
|
||||
"badges",
|
||||
"achievements",
|
||||
"sounds",
|
||||
],
|
||||
},
|
||||
{
|
||||
labelKey: "communityContent",
|
||||
icon: Newspaper,
|
||||
roots: [
|
||||
"articles",
|
||||
"photos",
|
||||
"events",
|
||||
"polls",
|
||||
"guilds",
|
||||
"media",
|
||||
"radio",
|
||||
"ads",
|
||||
"banners",
|
||||
"navigation",
|
||||
"help-questions",
|
||||
"writeable-boxes",
|
||||
"tags",
|
||||
"prefixes",
|
||||
],
|
||||
},
|
||||
{
|
||||
labelKey: "economy",
|
||||
icon: ShoppingCart,
|
||||
roots: [
|
||||
"shop",
|
||||
"marketplace",
|
||||
"transactions",
|
||||
"vouchers",
|
||||
"subscriptions",
|
||||
"calendar",
|
||||
],
|
||||
},
|
||||
{
|
||||
labelKey: "staffPermissions",
|
||||
icon: UsersRound,
|
||||
roots: ["teams", "applications", "permissions"],
|
||||
},
|
||||
{
|
||||
labelKey: "system",
|
||||
icon: Cog,
|
||||
roots: [
|
||||
"settings",
|
||||
"theme",
|
||||
"theme-builder",
|
||||
"maintenance",
|
||||
"favicon",
|
||||
"email-templates",
|
||||
"translations",
|
||||
"emulator",
|
||||
"housekeeping",
|
||||
"commandocentrum",
|
||||
],
|
||||
},
|
||||
{
|
||||
labelKey: "monitoring",
|
||||
icon: Activity,
|
||||
roots: ["logs", "analytics", "devops", "alerts"],
|
||||
},
|
||||
];
|
||||
|
||||
/** Owner order takes precedence; hidden entries are filtered before regrouping. */
|
||||
export function visibleAdminNavigation(
|
||||
options: AdminNavigationOptions,
|
||||
): AdminNavGroup[] {
|
||||
const allowed = new Set(options.allowedPermissions);
|
||||
const groups = applyAdminNavConfig(ADMIN_NAV_GROUPS, options.navConfig)
|
||||
.map((group) => ({
|
||||
...group,
|
||||
items: group.items.filter((item) =>
|
||||
navItemIsAllowed(item, {
|
||||
isSuperAdmin: options.isSuperAdmin,
|
||||
has: (slug) => allowed.has(slug),
|
||||
}),
|
||||
),
|
||||
}))
|
||||
.filter((group) => group.items.length);
|
||||
if (
|
||||
options.navConfig.groupOrder?.length ||
|
||||
Object.values(options.navConfig.itemOrder ?? {}).some(
|
||||
(order) => order.length,
|
||||
)
|
||||
)
|
||||
return groups;
|
||||
const remaining = new Map(
|
||||
groups.flatMap((group) => group.items).map((item) => [item.href, item]),
|
||||
);
|
||||
const organized = sections
|
||||
.map((section) => {
|
||||
const items = [...remaining.values()].filter((item) =>
|
||||
section.roots.includes(item.href.split("/")[2] ?? ""),
|
||||
);
|
||||
for (const item of items) remaining.delete(item.href);
|
||||
return { labelKey: section.labelKey, icon: section.icon, items };
|
||||
})
|
||||
.filter((group) => group.items.length);
|
||||
// New routes remain discoverable even before being assigned to an activity.
|
||||
if (remaining.size)
|
||||
organized.push({
|
||||
labelKey: "tools",
|
||||
icon: Cog,
|
||||
items: [...remaining.values()],
|
||||
});
|
||||
return organized;
|
||||
}
|
||||
|
||||
export function parseFavoritePages(raw: string | null): string[] {
|
||||
try {
|
||||
const value: unknown = JSON.parse(raw ?? "[]");
|
||||
return Array.isArray(value)
|
||||
? [
|
||||
...new Set(
|
||||
value.filter(
|
||||
(entry): entry is string =>
|
||||
typeof entry === "string" &&
|
||||
/^\/admin(?:\/[^?#]*)?$/.test(entry),
|
||||
),
|
||||
),
|
||||
].slice(0, 6)
|
||||
: [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user