From 29c14cbca2ef3d9cc46610f987b0cd3fae9aab27 Mon Sep 17 00:00:00 2001 From: devjayy43 Date: Mon, 28 Sep 2026 11:29:59 +0100 Subject: [PATCH] feat: Add responsive sidebar collapse toggle for desktop (#834) - Add SidebarContext to manage collapsed state globally - Add collapse/expand button in sidebar header - Sidebar collapses to icon-only view (w-20) when toggled - Navigation labels, logo text, and user info hidden when collapsed - Smooth transition animations for collapse/expand - Main content margin adjusts dynamically (lg:ml-64 -> lg:ml-20) - Mobile functionality preserved, collapse only visible on desktop - Accessible with proper aria-labels and title attributes for tooltips --- .../components/layout/DashboardAuthGuard.tsx | 15 ++++- frontend/components/layout/Sidebar.tsx | 61 ++++++++++++------- frontend/contexts/SidebarContext.tsx | 28 +++++++++ 3 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 frontend/contexts/SidebarContext.tsx diff --git a/frontend/components/layout/DashboardAuthGuard.tsx b/frontend/components/layout/DashboardAuthGuard.tsx index 37347291..ad066b90 100644 --- a/frontend/components/layout/DashboardAuthGuard.tsx +++ b/frontend/components/layout/DashboardAuthGuard.tsx @@ -6,11 +6,14 @@ import { usePathname, useRouter } from 'next/navigation'; import { Sidebar } from '@/components/layout/Sidebar'; import { Header } from '@/components/layout/Header'; import { ErrorBoundary } from '@/components/errors/ErrorBoundary'; +import { SidebarProvider, useSidebar } from '@/contexts/SidebarContext'; +import { cn } from '@/lib/utils'; -export function DashboardAuthGuard({ children }: { children: React.ReactNode }) { +function DashboardContent({ children }: { children: React.ReactNode }) { const isAuthenticated = useAuthStore((state) => state.isAuthenticated); const router = useRouter(); const pathname = usePathname(); + const { isCollapsed } = useSidebar(); const mainRef = React.useRef(null); const scrollPositions = React.useRef>({}); @@ -43,7 +46,7 @@ export function DashboardAuthGuard({ children }: { children: React.ReactNode }) return (
-
+
@@ -54,3 +57,11 @@ export function DashboardAuthGuard({ children }: { children: React.ReactNode })
); } + +export function DashboardAuthGuard({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} diff --git a/frontend/components/layout/Sidebar.tsx b/frontend/components/layout/Sidebar.tsx index b287f86e..5c3c5170 100644 --- a/frontend/components/layout/Sidebar.tsx +++ b/frontend/components/layout/Sidebar.tsx @@ -2,10 +2,11 @@ import Link from 'next/link'; import { usePathname, useRouter } from 'next/navigation'; -import { LayoutDashboard, Folder, FileText, Wallet, Scale, Menu, X, QrCode, ShieldCheck } from 'lucide-react'; +import { LayoutDashboard, Folder, FileText, Wallet, Scale, Menu, X, QrCode, ShieldCheck, ChevronLeft, ChevronRight } from 'lucide-react'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; +import { useSidebar } from '@/contexts/SidebarContext'; const navigation = [ { name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard }, @@ -21,6 +22,7 @@ export function Sidebar() { const pathname = usePathname(); const router = useRouter(); const [isMobileOpen, setIsMobileOpen] = useState(false); + const { isCollapsed, setIsCollapsed } = useSidebar(); return ( <> @@ -45,25 +47,38 @@ export function Sidebar() { role="navigation" aria-label="Main navigation" className={cn( - 'fixed inset-y-0 left-0 z-40 w-64 bg-white border-r border-gray-200 transform transition-transform duration-200 ease-in-out lg:translate-x-0', + 'fixed inset-y-0 left-0 z-40 bg-white border-r border-gray-200 transform transition-all duration-200 ease-in-out lg:translate-x-0', + isCollapsed ? 'w-20' : 'w-64', isMobileOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0' )} >
{/* Logo */} -
-
-