178 lines
5.2 KiB
JavaScript
178 lines
5.2 KiB
JavaScript
// [DEF:frontend.src.lib.components.layout.sidebarNavigation:Module]
|
|
// @COMPLEXITY: 3
|
|
// @SEMANTICS: navigation, sidebar, rbac, menu, filtering
|
|
// @PURPOSE: Build sidebar navigation categories filtered by current user permissions.
|
|
// @LAYER: UI
|
|
// @RELATION: DEPENDS_ON -> frontend.src.lib.auth.permissions.hasPermission
|
|
// @RELATION: USED_BY -> frontend.src.lib.components.layout.Sidebar
|
|
// @INVARIANT: Admin role can access all categories and subitems through permission utility.
|
|
|
|
import { hasPermission } from "$lib/auth/permissions.js";
|
|
|
|
// [DEF:isItemAllowed:Function]
|
|
// @PURPOSE: Check whether a single menu node can be shown for a given user.
|
|
// @PRE: item can contain optional requiredPermission/requiredAction.
|
|
// @POST: Returns true when no permission is required or permission check passes.
|
|
function isItemAllowed(user, item) {
|
|
if (!item?.requiredPermission) return true;
|
|
return hasPermission(
|
|
user,
|
|
item.requiredPermission,
|
|
item.requiredAction || "READ",
|
|
);
|
|
}
|
|
// [/DEF:isItemAllowed:Function]
|
|
|
|
// [DEF:buildSidebarCategories:Function]
|
|
// @PURPOSE: Build translated sidebar categories and filter them by RBAC permissions.
|
|
// @PRE: i18nState provides nav labels; user can be null.
|
|
// @POST: Returns only categories/subitems available for provided user.
|
|
export function buildSidebarCategories(i18nState, user) {
|
|
const nav = i18nState?.nav || {};
|
|
|
|
const categories = [
|
|
{
|
|
id: "dashboards",
|
|
label: nav.dashboards,
|
|
icon: "dashboard",
|
|
tone: "from-sky-100 to-sky-200 text-sky-700 ring-sky-200",
|
|
path: "/dashboards",
|
|
requiredPermission: "plugin:migration",
|
|
requiredAction: "READ",
|
|
subItems: [
|
|
{
|
|
label: nav.overview,
|
|
path: "/dashboards",
|
|
requiredPermission: "plugin:migration",
|
|
requiredAction: "READ",
|
|
},
|
|
{
|
|
label: nav.health_center,
|
|
path: "/dashboards/health",
|
|
requiredPermission: "plugin:migration",
|
|
requiredAction: "READ",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "datasets",
|
|
label: nav.datasets,
|
|
icon: "database",
|
|
tone: "from-emerald-100 to-emerald-200 text-emerald-700 ring-emerald-200",
|
|
path: "/datasets",
|
|
requiredPermission: "plugin:migration",
|
|
requiredAction: "READ",
|
|
subItems: [
|
|
{
|
|
label: nav.all_datasets,
|
|
path: "/datasets",
|
|
requiredPermission: "plugin:migration",
|
|
requiredAction: "READ",
|
|
},
|
|
{
|
|
label: nav.dataset_review_workspace,
|
|
path: "/datasets/review",
|
|
requiredPermission: "plugin:migration",
|
|
requiredAction: "READ",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "storage",
|
|
label: nav.storage,
|
|
icon: "storage",
|
|
tone: "from-amber-100 to-amber-200 text-amber-800 ring-amber-200",
|
|
path: "/storage",
|
|
requiredPermission: "plugin:storage",
|
|
requiredAction: "READ",
|
|
subItems: [
|
|
{
|
|
label: nav.backups,
|
|
path: "/storage/backups",
|
|
requiredPermission: "plugin:storage",
|
|
requiredAction: "READ",
|
|
},
|
|
{
|
|
label: nav.repositories,
|
|
path: "/storage/repos",
|
|
requiredPermission: "plugin:storage",
|
|
requiredAction: "READ",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "reports",
|
|
label: nav.reports,
|
|
icon: "reports",
|
|
tone: "from-violet-100 to-fuchsia-100 text-violet-700 ring-violet-200",
|
|
path: "/reports",
|
|
requiredPermission: "tasks",
|
|
requiredAction: "READ",
|
|
subItems: [
|
|
{
|
|
label: nav.reports,
|
|
path: "/reports",
|
|
requiredPermission: "tasks",
|
|
requiredAction: "READ",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: "profile",
|
|
label: nav.profile,
|
|
icon: "admin",
|
|
tone: "from-indigo-100 to-indigo-200 text-indigo-700 ring-indigo-200",
|
|
path: "/profile",
|
|
subItems: [{ label: nav.profile, path: "/profile" }],
|
|
},
|
|
{
|
|
id: "admin",
|
|
label: nav.admin,
|
|
icon: "admin",
|
|
tone: "from-rose-100 to-rose-200 text-rose-700 ring-rose-200",
|
|
path: "/admin",
|
|
subItems: [
|
|
{
|
|
label: nav.admin_users,
|
|
path: "/admin/users",
|
|
requiredPermission: "admin:users",
|
|
requiredAction: "READ",
|
|
},
|
|
{
|
|
label: nav.admin_roles,
|
|
path: "/admin/roles",
|
|
requiredPermission: "admin:roles",
|
|
requiredAction: "READ",
|
|
},
|
|
{
|
|
label: nav.settings,
|
|
path: "/settings",
|
|
requiredPermission: "admin:settings",
|
|
requiredAction: "READ",
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
return categories
|
|
.map((category) => {
|
|
const visibleSubItems = (category.subItems || []).filter((subItem) =>
|
|
isItemAllowed(user, subItem),
|
|
);
|
|
return {
|
|
...category,
|
|
subItems: visibleSubItems,
|
|
};
|
|
})
|
|
.filter((category) => {
|
|
const categoryVisible = isItemAllowed(user, category);
|
|
if (!categoryVisible) return false;
|
|
|
|
const hasVisibleSubItems =
|
|
Array.isArray(category.subItems) && category.subItems.length > 0;
|
|
return hasVisibleSubItems;
|
|
});
|
|
}
|
|
// [/DEF:buildSidebarCategories:Function]
|
|
|
|
// [/DEF:frontend.src.lib.components.layout.sidebarNavigation:Module]
|