From 14a1073c92adb63b1bcc5096b40e78b989c33008 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Wed, 29 Jul 2026 10:17:03 +0200 Subject: [PATCH] =?UTF-8?q?hotfix:=20sidebar=20+=20topbar=20canAccess=20fa?= =?UTF-8?q?llback=20=E2=80=94=20show=20all=20items=20for=20system=5Fadmin?= =?UTF-8?q?=20or=20empty=20permissions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/layout/Sidebar.tsx | 18 ++++++++++++++---- frontend/src/components/layout/TopBar.tsx | 12 +++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/frontend/src/components/layout/Sidebar.tsx b/frontend/src/components/layout/Sidebar.tsx index 30d6ea1..2600c5b 100644 --- a/frontend/src/components/layout/Sidebar.tsx +++ b/frontend/src/components/layout/Sidebar.tsx @@ -42,6 +42,16 @@ export function Sidebar() { const manifests = usePluginStore(s => s.manifests); const { data: menuOrderData } = useMenuOrder(); const { hasPermission } = usePermission(); + const user = useAuthStore((state) => state.user); + + // Fallback: system admin or empty permissions = show everything + const canAccess = (perm?: string): boolean => { + if (!perm) return true; + if (user?.is_system_admin) return true; + const perms = user?.permissions || []; + if (perms.length === 0) return true; // No permissions loaded — show all, backend 403 handles it + return hasPermission(perm); + }; const allMenuItems = useMemo(() => { const staticItems = singleItems.map(item => ({ @@ -57,7 +67,7 @@ export function Sidebar() { const pluginItems = manifests .flatMap((m) => m.menu_items) - .filter(item => !item.permission || hasPermission(item.permission)) + .filter(item => !item.permission || canAccess(item.permission)) .map(item => ({ path: item.path, labelKey: item.label_key, @@ -94,7 +104,7 @@ export function Sidebar() { if (a.order !== b.order) return a.order - b.order; return a.label.localeCompare(b.label); }); - }, [manifests, menuOrderData]); + }, [manifests, menuOrderData, user]); const [expandedItems, setExpandedItems] = useState>(new Set()); @@ -158,7 +168,7 @@ export function Sidebar() { const elements: React.ReactNode[] = []; for (const item of singles) { // Skip if user lacks permission - if (item.permission && !hasPermission(item.permission)) continue; + if (item.permission && !canAccess(item.permission)) continue; elements.push(
  • !item.permission || hasPermission(item.permission)); + const visibleItems = items.filter(item => !item.permission || canAccess(item.permission)); if (visibleItems.length === 0) continue; // Hide empty groups const groupKey = `plugin-group-${group}`; const expanded = expandedItems.has(groupKey); diff --git a/frontend/src/components/layout/TopBar.tsx b/frontend/src/components/layout/TopBar.tsx index f6372c6..e075102 100644 --- a/frontend/src/components/layout/TopBar.tsx +++ b/frontend/src/components/layout/TopBar.tsx @@ -22,6 +22,12 @@ export function TopBar() { const logoutMutation = useLogout(); const minimizedWindows = useWindowStore((s) => s.windows.filter((w) => w.state === 'minimized')); const { hasPermission } = usePermission(); + const canAccess = (perm: string): boolean => { + if (user?.is_system_admin) return true; + const perms = user?.permissions || []; + if (perms.length === 0) return true; + return hasPermission(perm); + }; const restoreWindow = useWindowStore((s) => s.restoreWindow); const [userMenuOpen, setUserMenuOpen] = useState(false); @@ -85,7 +91,7 @@ export function TopBar() {
    {/* Quick Create */} - {hasPermission('contacts:write') && ( + {canAccess('contacts:write') && ( - {hasPermission('settings:read') && ( + {canAccess('settings:read') && ( - {hasPermission('audit:read') && ( + {canAccess('audit:read') && (