From e3d9a3bd18d3d127c6829eb797e4a0314dbcf758 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Thu, 9 Apr 2026 19:49:42 +0200 Subject: [PATCH] fix: sidebar active state, breadcrumbs, collapse, username fallback, lucide icons Co-Authored-By: Claude Sonnet 4.6 --- ui/src/components/Layout.tsx | 78 ++++++++++++++---------------------- 1 file changed, 30 insertions(+), 48 deletions(-) diff --git a/ui/src/components/Layout.tsx b/ui/src/components/Layout.tsx index 73b4d05..7f93f50 100644 --- a/ui/src/components/Layout.tsx +++ b/ui/src/components/Layout.tsx @@ -1,4 +1,6 @@ -import { Outlet, useNavigate } from 'react-router'; +import { useState, useMemo } from 'react'; +import { Outlet, useNavigate, useLocation } from 'react-router'; +import { LayoutDashboard, ShieldCheck, Building, Activity } from 'lucide-react'; import { AppShell, Sidebar, @@ -21,54 +23,23 @@ function CameleerLogo() { ); } -// Nav icon helpers -function DashboardIcon() { - return ( - - ); -} - -function LicenseIcon() { - return ( - - ); -} - -function ObsIcon() { - return ( - - ); -} - -function PlatformIcon() { - return ( - - ); -} - export function Layout() { const navigate = useNavigate(); + const location = useLocation(); const { logout } = useAuth(); const scopes = useScopes(); const { username } = useOrgStore(); + const [collapsed, setCollapsed] = useState(false); + + const breadcrumb = useMemo(() => { + if (location.pathname.startsWith('/admin')) return [{ label: 'Admin' }, { label: 'Tenants' }]; + if (location.pathname.startsWith('/license')) return [{ label: 'License' }]; + return [{ label: 'Dashboard' }]; + }, [location.pathname]); + const sidebar = ( - {}}> + setCollapsed(c => !c)}> } title="Cameleer SaaS" @@ -77,9 +48,10 @@ export function Layout() { {/* Dashboard */} } + icon={} label="Dashboard" open={false} + active={location.pathname === '/' || location.pathname === ''} onToggle={() => navigate('/')} > {null} @@ -87,9 +59,10 @@ export function Layout() { {/* License */} } + icon={} label="License" open={false} + active={location.pathname.startsWith('/license')} onToggle={() => navigate('/license')} > {null} @@ -98,9 +71,10 @@ export function Layout() { {/* Platform Admin section */} {scopes.has('platform:admin') && ( } + icon={} label="Platform" open={false} + active={location.pathname.startsWith('/admin')} onToggle={() => navigate('/admin/tenants')} > {null} @@ -110,7 +84,7 @@ export function Layout() { {/* Link to the server observability dashboard */} } + icon={} label="Open Server Dashboard" onClick={() => window.open('/server/', '_blank', 'noopener')} /> @@ -120,9 +94,17 @@ export function Layout() { return ( + {/* + * TopBar always renders status filters, time range pills, auto-refresh, and + * command palette search via useGlobalFilters() / useCommandPalette(). Both + * hooks throw if their providers are absent, so GlobalFilterProvider and + * CommandPaletteProvider cannot be removed from main.tsx without crashing the + * app. The TopBar API has no props to suppress these server-oriented controls. + * Hiding them on platform pages would require a DS change. + */}