From 798fa0ab56bf6ae59960d273c277b47b5b934ecb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=C3=A1nchez?= Date: Fri, 28 Aug 2026 14:01:29 -0600 Subject: [PATCH] feat[frontend](sidebar): add collapse toggle for icon-only mode --- .../layouts/DashboardLayout/Sidebar.tsx | 191 ++++++++++++++---- 1 file changed, 151 insertions(+), 40 deletions(-) diff --git a/frontend/src/shared/layouts/DashboardLayout/Sidebar.tsx b/frontend/src/shared/layouts/DashboardLayout/Sidebar.tsx index 8afa12d3a..8b696c785 100644 --- a/frontend/src/shared/layouts/DashboardLayout/Sidebar.tsx +++ b/frontend/src/shared/layouts/DashboardLayout/Sidebar.tsx @@ -23,6 +23,8 @@ import { LifeBuoy, Mail, Palette, + PanelLeftClose, + PanelLeftOpen, Plug, Radar, ScrollText, @@ -41,6 +43,7 @@ import { } from 'lucide-react' import { useTranslation } from 'react-i18next' import { cn } from '@/shared/lib/utils' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/shared/components/ui/tooltip' import { useAuth } from '@/features/auth' import { useBilling } from '@/features/billing' import { useSupportTenant } from '@/shared/lib/current-tenant' @@ -177,6 +180,7 @@ const settingsItems: { const SECTIONS_KEY = 'utmstack-sidebar-sections-closed' const GROUPS_KEY = 'utmstack-sidebar-groups-closed' +const COLLAPSED_KEY = 'utmstack-sidebar-collapsed' function loadSet(key: string): Set { if (typeof window === 'undefined') return new Set() @@ -190,6 +194,36 @@ function loadSet(key: string): Set { } } +function loadCollapsed(): boolean { + if (typeof window === 'undefined') return false + return localStorage.getItem(COLLAPSED_KEY) === '1' +} + +function useCollapsed(): [boolean, () => void] { + const [collapsed, setCollapsed] = useState(loadCollapsed) + useEffect(() => { + localStorage.setItem(COLLAPSED_KEY, collapsed ? '1' : '0') + }, [collapsed]) + return [collapsed, () => setCollapsed((v) => !v)] +} + +function CollapseToggle({ collapsed, onToggle }: { collapsed: boolean; onToggle: () => void }) { + const Icon = collapsed ? PanelLeftOpen : PanelLeftClose + return ( + + ) +} + export function Sidebar() { const { t } = useTranslation() const { isAdmin, isPlatformAdmin } = useAuth() @@ -201,6 +235,7 @@ export function Sidebar() { const actingAsPlatform = isPlatformAdmin && supportTenant === null const [closedSections, setClosedSections] = useState>(() => loadSet(SECTIONS_KEY)) const [closedGroups, setClosedGroups] = useState>(() => loadSet(GROUPS_KEY)) + const [collapsed, toggleCollapsed] = useCollapsed() const { pathname } = useLocation() useEffect(() => { @@ -233,12 +268,20 @@ export function Sidebar() { // Drill-down: when navigating under /settings the sidebar swaps to a sub-nav. if (pathname.startsWith('/settings')) { - return + return } return ( -