sprint9: app visibility — sidebar permission filter + TopBar + ProtectedRoute + route guards
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
This commit is contained in:
@@ -7,6 +7,7 @@ import { ChevronRight, FileText, Home, Settings, Users } from 'lucide-react';
|
||||
import { usePluginStore } from '@/store/pluginStore';
|
||||
import * as LucideIcons from 'lucide-react';
|
||||
import { useMenuOrder } from '@/api/users';
|
||||
import { usePermission } from '@/hooks/usePermission';
|
||||
|
||||
interface NavSingleItem {
|
||||
to: string;
|
||||
@@ -40,6 +41,7 @@ export function Sidebar() {
|
||||
const location = useLocation();
|
||||
const manifests = usePluginStore(s => s.manifests);
|
||||
const { data: menuOrderData } = useMenuOrder();
|
||||
const { hasPermission } = usePermission();
|
||||
|
||||
const allMenuItems = useMemo(() => {
|
||||
const staticItems = singleItems.map(item => ({
|
||||
@@ -50,10 +52,12 @@ export function Sidebar() {
|
||||
order: item.order,
|
||||
group: undefined as string | undefined,
|
||||
isStatic: true as const,
|
||||
permission: item.to === '/dashboard' ? 'dashboard:read' : item.to === '/contacts' ? 'contacts:read' : undefined,
|
||||
}));
|
||||
|
||||
const pluginItems = manifests
|
||||
.flatMap((m) => m.menu_items)
|
||||
.filter(item => !item.permission || hasPermission(item.permission))
|
||||
.map(item => ({
|
||||
path: item.path,
|
||||
labelKey: item.label_key,
|
||||
@@ -62,6 +66,7 @@ export function Sidebar() {
|
||||
order: item.order,
|
||||
group: item.group,
|
||||
isStatic: false as const,
|
||||
permission: item.permission,
|
||||
}));
|
||||
|
||||
const allItems = [...staticItems, ...pluginItems];
|
||||
@@ -152,6 +157,8 @@ export function Sidebar() {
|
||||
}
|
||||
const elements: React.ReactNode[] = [];
|
||||
for (const item of singles) {
|
||||
// Skip if user lacks permission
|
||||
if (item.permission && !hasPermission(item.permission)) continue;
|
||||
elements.push(
|
||||
<li key={item.path}>
|
||||
<NavLink
|
||||
@@ -175,6 +182,9 @@ export function Sidebar() {
|
||||
);
|
||||
}
|
||||
for (const [group, items] of groups) {
|
||||
// Filter group items by permission
|
||||
const visibleItems = items.filter(item => !item.permission || hasPermission(item.permission));
|
||||
if (visibleItems.length === 0) continue; // Hide empty groups
|
||||
const groupKey = `plugin-group-${group}`;
|
||||
const expanded = expandedItems.has(groupKey);
|
||||
elements.push(
|
||||
@@ -196,13 +206,13 @@ export function Sidebar() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{getIcon(items[0].icon as string)}
|
||||
{getIcon(visibleItems[0].icon as string)}
|
||||
<span className="flex-1 truncate">{group}</span>
|
||||
{chevronIcon(expanded)}
|
||||
</div>
|
||||
{expanded && (
|
||||
<ul className="mt-1 ml-4 space-y-1 border-l border-secondary-700 pl-2" role="group">
|
||||
{items.map((child) => (
|
||||
{visibleItems.map((child) => (
|
||||
<li key={child.path}>
|
||||
<NavLink
|
||||
to={child.path}
|
||||
|
||||
Reference in New Issue
Block a user