From fe4b16ab72df7e41098d1bbd160be84419aaa98a Mon Sep 17 00:00:00 2001 From: DevSolex Date: Sun, 31 May 2026 00:30:13 +0100 Subject: [PATCH] feat: implement responsive sidebar navigation - Add mobile drawer with overlay and desktop sticky sidebar - Support expand/collapse toggle (w-14 collapsed, w-64 expanded) - Highlight active route using usePathname() - Wire Sidebar into dashboard page with mobile menu button Closes #248 --- src/app/dashboard/page.tsx | 21 +- src/components/dashboard/Sidebar.tsx | 322 +++++++++++------- .../dashboard/__tests__/Sidebar.test.tsx | 75 ++++ 3 files changed, 287 insertions(+), 131 deletions(-) create mode 100644 src/components/dashboard/__tests__/Sidebar.test.tsx diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index c86ed4f7..07a4cc61 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -14,6 +14,9 @@ import { useKycStore } from "@/store/kycStore"; import Link from "next/link"; import { TransactionSecuritySettings } from "@/components/security/TransactionSecuritySettings"; import { Skeleton } from "@/components/ui/skeleton"; +import { Sidebar } from "@/components/dashboard/Sidebar"; +import { Menu } from "lucide-react"; +import { Button } from "@/components/ui/button"; const StakingPanel = dynamic( () => import("@/components/dashboard/StakingPanel").then((m) => m.StakingPanel), @@ -62,17 +65,33 @@ const DataRefreshWrapper = dynamic( const Index = () => { const [sidebarOpen, setSidebarOpen] = useState(false); - const [activeItem, setActiveItem] = useState("dashboard"); + const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const { profile } = useKycStore(); return (
+ setSidebarOpen(false)} + onToggleCollapse={() => setSidebarCollapsed((c) => !c)} + />
+ {/* Mobile menu toggle */} +
PC
diff --git a/src/components/dashboard/Sidebar.tsx b/src/components/dashboard/Sidebar.tsx index 3a2cc5ef..bc5918b0 100644 --- a/src/components/dashboard/Sidebar.tsx +++ b/src/components/dashboard/Sidebar.tsx @@ -1,140 +1,202 @@ -// 'use client'; +'use client'; -// import { motion, AnimatePresence } from "framer-motion"; -// import { -// LayoutDashboard, -// Building2, -// BarChart3, -// Wallet, -// FileText, -// Settings, -// HelpCircle, -// ChevronLeft, -// X -// } from "lucide-react"; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { + LayoutDashboard, + Building2, + BarChart3, + Wallet, + FileText, + Settings, + HelpCircle, + X, + ChevronLeft, +} from 'lucide-react'; +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; -// interface SidebarProps { -// isOpen: boolean; -// onClose: () => void; -// activeItem?: string; -// onItemClick?: (item: string) => void; -// } +const navItems = [ + { href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard }, + { href: '/properties', label: 'Properties', icon: Building2 }, + { href: '/dashboard?tab=analytics', label: 'Analytics', icon: BarChart3 }, + { href: '/dashboard?tab=wallet', label: 'Wallet', icon: Wallet }, + { href: '/tax-report', label: 'Reports', icon: FileText }, +]; -// const menuItems = [ -// { id: "dashboard", label: "Dashboard", icon: LayoutDashboard }, -// { id: "properties", label: "Properties", icon: Building2 }, -// { id: "analytics", label: "Analytics", icon: BarChart3 }, -// { id: "wallet", label: "Wallet", icon: Wallet }, -// { id: "reports", label: "Reports", icon: FileText }, -// ]; +const bottomItems = [ + { href: '/dashboard?tab=settings', label: 'Settings', icon: Settings }, + { href: '/dashboard?tab=help', label: 'Help & Support', icon: HelpCircle }, +]; -// const bottomItems = [ -// { id: "settings", label: "Settings", icon: Settings }, -// { id: "help", label: "Help & Support", icon: HelpCircle }, -// ]; +interface SidebarProps { + isOpen: boolean; + isCollapsed: boolean; + onClose: () => void; + onToggleCollapse: () => void; +} -// export const Sidebar = ({ isOpen, onClose, activeItem = "dashboard", onItemClick }: SidebarProps) => { -// const SidebarContent = () => ( -//
-// {/* Logo section for mobile */} -//
-//
-//
-// M -//
-//
-//

MettaChain

-//
-//
-// -//
+function NavLink({ + href, + label, + icon: Icon, + isActive, + isCollapsed, + onClick, +}: { + href: string; + label: string; + icon: React.ElementType; + isActive: boolean; + isCollapsed: boolean; + onClick?: () => void; +}) { + return ( + + + {!isCollapsed && ( + <> + {label} + {isActive &&
} + + )} + + ); +} -// {/* Navigation */} -// +function SidebarContent({ + isCollapsed, + onClose, + onToggleCollapse, + isMobileDrawer, +}: { + isCollapsed: boolean; + onClose: () => void; + onToggleCollapse: () => void; + isMobileDrawer?: boolean; +}) { + const pathname = usePathname(); -// {/* Bottom section */} -//
-// {bottomItems.map((item) => ( -// -// ))} -//
+ return ( +
+ {/* Header */} +
+ {(!isCollapsed || isMobileDrawer) && ( +
+
+ PC +
+ PropChain +
+ )} + {isMobileDrawer ? ( + + ) : ( + + )} +
-// {/* Upgrade card */} -//
-//
-//

Upgrade to Pro

-//

Unlock advanced analytics and premium features

-// -//
-//
-//
-// ); + {/* Main nav */} + -// return ( -// <> -// {/* Desktop sidebar */} -// + {/* Bottom nav */} +
+ {bottomItems.map((item) => ( + + ))} +
+
+ ); +} -// {/* Mobile sidebar overlay */} -// -// {isOpen && ( -// <> -// -// -// -// -// -// )} -// -// -// ); -// }; +export function Sidebar({ isOpen, isCollapsed, onClose, onToggleCollapse }: SidebarProps) { + return ( + <> + {/* Desktop sidebar */} + + + {/* Mobile overlay */} + {isOpen && ( +