1+ "use client" ;
2+
3+ import { useState , useEffect , useCallback } from "react" ;
4+ import { usePathname } from "next/navigation" ;
5+ import { cn } from "@/lib/utils" ;
6+ import { ScrollArea } from "@/components/ui/scroll-area" ;
7+ import { GlobeIcon , XIcon , HouseIcon , ClockIcon , UsersIcon , MapPinIcon , ListIcon , InfoIcon , GitBranchIcon , BugIcon , FunnelIcon } from "@phosphor-icons/react" ;
8+ import { Button } from "@/components/ui/button" ;
9+ import Link from "next/link" ;
10+ import { Logo } from "@/components/layout/logo" ;
11+ import { ThemeToggle } from "@/components/layout/theme-toggle" ;
12+ import { UserMenu } from "@/components/layout/user-menu" ;
13+ import { NotificationsPopover } from "@/components/notifications/notifications-popover" ;
14+
15+ const demoNavigation = [
16+ {
17+ title : "Analytics" ,
18+ items : [
19+ { name : "Overview" , icon : HouseIcon , href : "/demo/OXmNQsViBT-FOS_wZCTHc" , highlight : true } ,
20+ { name : "Sessions" , icon : ClockIcon , href : "/demo/OXmNQsViBT-FOS_wZCTHc/sessions" , highlight : true } ,
21+ { name : "Funnels" , icon : FunnelIcon , href : "/demo/OXmNQsViBT-FOS_wZCTHc/funnels" , highlight : true } ,
22+ { name : "Journeys" , icon : GitBranchIcon , href : "/demo/OXmNQsViBT-FOS_wZCTHc/journeys" , highlight : true } ,
23+ { name : "Errors" , icon : BugIcon , href : "/demo/OXmNQsViBT-FOS_wZCTHc/errors" , highlight : true } ,
24+ { name : "Profiles" , icon : UsersIcon , href : "/demo/OXmNQsViBT-FOS_wZCTHc/profiles" , highlight : true } ,
25+ { name : "Map" , icon : MapPinIcon , href : "/demo/OXmNQsViBT-FOS_wZCTHc/map" , highlight : true } ,
26+ ] ,
27+ }
28+ ] ;
29+
30+
31+ export function Sidebar ( ) {
32+ const pathname = usePathname ( ) ;
33+ const [ isMobileOpen , setIsMobileOpen ] = useState ( false ) ;
34+
35+ const closeSidebar = useCallback ( ( ) => {
36+ setIsMobileOpen ( false ) ;
37+ } , [ ] ) ;
38+
39+ // Handle keyboard navigation
40+ useEffect ( ( ) => {
41+ const handleKeyDown = ( e : KeyboardEvent ) => {
42+ if ( e . key === 'Escape' && isMobileOpen ) {
43+ closeSidebar ( ) ;
44+ }
45+ } ;
46+
47+ document . addEventListener ( 'keydown' , handleKeyDown ) ;
48+ return ( ) => document . removeEventListener ( 'keydown' , handleKeyDown ) ;
49+ } , [ isMobileOpen , closeSidebar ] ) ;
50+
51+ return (
52+ < >
53+ { /* Top Header */ }
54+ < header className = "fixed top-0 left-0 right-0 z-50 w-full h-16 border-b bg-background/95 backdrop-blur-md" >
55+ < div className = "flex items-center h-full px-4 md:px-6" >
56+ { /* Left side: Logo + Mobile menu */ }
57+ < div className = "flex items-center gap-4" >
58+ < Button
59+ variant = "ghost"
60+ size = "icon"
61+ className = "md:hidden"
62+ onClick = { ( ) => setIsMobileOpen ( true ) }
63+ >
64+ < ListIcon size = { 32 } weight = "duotone" className = "h-5 w-5" />
65+ < span className = "sr-only" > Toggle menu</ span >
66+ </ Button >
67+
68+ < div className = "flex items-center gap-3" >
69+ < div className = "flex flex-row items-center gap-3" >
70+ < Logo />
71+ </ div >
72+ </ div >
73+ </ div >
74+
75+ { /* Right Side - User Controls */ }
76+ < div className = "flex items-center gap-2 ml-auto" >
77+ < ThemeToggle />
78+
79+ { /* Help */ }
80+ < Button
81+ variant = "ghost"
82+ size = "icon"
83+ className = "hidden md:flex h-8 w-8"
84+ >
85+ < InfoIcon size = { 32 } weight = "duotone" className = "h-6 w-6" />
86+ < span className = "sr-only" > Help</ span >
87+ </ Button >
88+
89+ { /* Notifications */ }
90+ < NotificationsPopover />
91+
92+ { /* User Menu */ }
93+ < UserMenu />
94+ </ div >
95+ </ div >
96+ </ header >
97+
98+ { /* Mobile backdrop */ }
99+ { isMobileOpen && (
100+ < div
101+ className = "fixed inset-0 bg-black/20 z-30 md:hidden"
102+ onClick = { closeSidebar }
103+ />
104+ ) }
105+
106+ { /* Sidebar */ }
107+ < div
108+ className = { cn (
109+ "fixed inset-y-0 left-0 z-40 w-64 bg-background" ,
110+ "border-r transition-transform duration-200 ease-out md:translate-x-0 pt-16" ,
111+ isMobileOpen ? "translate-x-0" : "-translate-x-full"
112+ ) }
113+ >
114+ { /* Mobile close button */ }
115+ < Button
116+ variant = "ghost"
117+ size = "sm"
118+ className = "absolute top-3 right-3 z-50 md:hidden h-8 w-8 p-0"
119+ onClick = { closeSidebar }
120+ >
121+ < XIcon size = { 32 } weight = "duotone" className = "h-4 w-4" />
122+ < span className = "sr-only" > Close sidebar</ span >
123+ </ Button >
124+
125+ < ScrollArea className = "h-[calc(100vh-4rem)]" >
126+ < div className = "p-3 space-y-4" >
127+ { /* Demo Website Header */ }
128+ < div className = "flex items-center gap-3 p-3 bg-muted/50 rounded border" >
129+ < div className = "p-2 rounded bg-primary/10 border border-primary/20" >
130+ < GlobeIcon size = { 32 } weight = "duotone" className = "h-5 w-5 text-primary" />
131+ </ div >
132+ < div className = "min-w-0 flex-1" >
133+ < h2 className = "font-semibold text-sm truncate" > Landing Page</ h2 >
134+ < Link href = "https://www.databuddy.cc" target = "_blank" className = "text-xs text-muted-foreground truncate" > www.databuddy.cc</ Link >
135+ </ div >
136+ </ div >
137+
138+ { /* Demo Navigation */ }
139+ { demoNavigation . map ( ( section ) => (
140+ < div key = { section . title } >
141+ < h3 className = "px-2 mb-2 text-xs font-semibold text-muted-foreground tracking-wider uppercase" >
142+ { section . title }
143+ </ h3 >
144+ < div className = "space-y-1 ml-1" >
145+ { section . items . map ( ( item ) => {
146+ const isActive = pathname === item . href ;
147+ const Icon = item . icon ;
148+
149+ return (
150+ < Link
151+ key = { item . name }
152+ href = { item . href }
153+ className = { cn (
154+ "flex items-center gap-3 px-3 py-2 text-sm rounded transition-all cursor-pointer" ,
155+ isActive
156+ ? "bg-primary/15 text-primary font-medium"
157+ : "text-foreground hover:bg-accent/70"
158+ ) }
159+ >
160+ < Icon size = { 32 } weight = "duotone" className = { cn ( "h-4 w-4" , isActive && "text-primary" ) } />
161+ < span className = "truncate" > { item . name } </ span >
162+ </ Link >
163+ ) ;
164+ } ) }
165+ </ div >
166+ </ div >
167+ ) ) }
168+ </ div >
169+ </ ScrollArea >
170+ </ div >
171+ </ >
172+ ) ;
173+ }
0 commit comments