hotfix: sidebar + topbar canAccess fallback — show all items for system_admin or empty permissions
This commit is contained in:
@@ -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<Set<string>>(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(
|
||||
<li key={item.path}>
|
||||
<NavLink
|
||||
@@ -183,7 +193,7 @@ export function Sidebar() {
|
||||
}
|
||||
for (const [group, items] of groups) {
|
||||
// Filter group items by permission
|
||||
const visibleItems = items.filter(item => !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);
|
||||
|
||||
@@ -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() {
|
||||
<div className="flex items-center gap-2">
|
||||
<NotificationBell />
|
||||
{/* Quick Create */}
|
||||
{hasPermission('contacts:write') && (
|
||||
{canAccess('contacts:write') && (
|
||||
<button
|
||||
onClick={() => navigate('/contacts/new')}
|
||||
className="flex items-center gap-1.5 bg-primary-600 text-white px-3 py-1.5 rounded-md text-sm font-medium hover:bg-primary-700 min-h-touch focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
||||
@@ -144,7 +150,7 @@ export function TopBar() {
|
||||
>
|
||||
{t('topbar.profile')}
|
||||
</button>
|
||||
{hasPermission('settings:read') && (
|
||||
{canAccess('settings:read') && (
|
||||
<button
|
||||
onClick={() => { setUserMenuOpen(false); navigate('/settings'); }}
|
||||
className="w-full text-left px-3 py-2 text-sm hover:bg-secondary-50 min-h-touch focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
||||
@@ -169,7 +175,7 @@ export function TopBar() {
|
||||
<Bot className="w-4 h-4" aria-hidden="true" strokeWidth={2} />
|
||||
{t('nav.agents', 'Agenten')}
|
||||
</button>
|
||||
{hasPermission('audit:read') && (
|
||||
{canAccess('audit:read') && (
|
||||
<button
|
||||
onClick={() => { setUserMenuOpen(false); navigate('/audit-log'); }}
|
||||
className="w-full text-left px-3 py-2 text-sm hover:bg-secondary-50 min-h-touch focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-500"
|
||||
|
||||
Reference in New Issue
Block a user