1+ import { useState , useRef , useEffect } from 'react' ;
2+ import { Bell , Search , User , Settings , LogOut , ChevronDown , Menu , X } from 'lucide-react' ;
3+
4+ export const Header = ( ) => {
5+ const [ isProfileOpen , setIsProfileOpen ] = useState ( false ) ;
6+ const [ isMobileMenuOpen , setIsMobileMenuOpen ] = useState ( false ) ;
7+ const [ showSuggestions , setShowSuggestions ] = useState ( false ) ;
8+ const [ searchQuery , setSearchQuery ] = useState ( '' ) ;
9+
10+ const profileRef = useRef < HTMLDivElement > ( null ) ;
11+ const searchRef = useRef < HTMLDivElement > ( null ) ;
12+
13+ // Close dropdowns when clicking outside
14+ useEffect ( ( ) => {
15+ const handleClickOutside = ( event : MouseEvent ) => {
16+ if ( profileRef . current && ! profileRef . current . contains ( event . target as Node ) ) {
17+ setIsProfileOpen ( false ) ;
18+ }
19+ if ( searchRef . current && ! searchRef . current . contains ( event . target as Node ) ) {
20+ setShowSuggestions ( false ) ;
21+ }
22+ } ;
23+
24+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
25+ return ( ) => document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
26+ } , [ ] ) ;
27+
28+ // Mock search suggestions
29+ const searchSuggestions = [
30+ "Mean Reversion Strategy" ,
31+ "BTC/USD Correlation" ,
32+ "Portfolio Optimization" ,
33+ "Risk Metrics Analysis" ,
34+ "Volatility Forecasting"
35+ ] ;
36+
37+ const filteredSuggestions = searchSuggestions . filter ( suggestion =>
38+ suggestion . toLowerCase ( ) . includes ( searchQuery . toLowerCase ( ) )
39+ ) ;
40+
41+ // Handle mobile search
42+ const handleMobileSearch = ( ) => {
43+ // Implement mobile search modal or expandable search
44+ console . log ( "Mobile search activated" ) ;
45+ } ;
46+
47+ // Handle profile actions
48+ const handleProfileAction = ( action : string ) => {
49+ setIsProfileOpen ( false ) ;
50+ console . log ( `${ action } clicked` ) ;
51+ // Add your navigation or action logic here
52+ } ;
53+
54+ return (
55+ < header className = "bg-white dark:bg-gray-900 shadow-sm border-b border-gray-200 dark:border-gray-700 sticky top-0 z-50" >
56+ < div className = "flex items-center justify-between px-4 lg:px-6 py-4" >
57+ { /* Logo and Mobile Menu Button */ }
58+ < div className = "flex items-center space-x-4" >
59+ { /* Mobile Menu Button */ }
60+ < button
61+ onClick = { ( ) => setIsMobileMenuOpen ( ! isMobileMenuOpen ) }
62+ className = "lg:hidden p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
63+ aria-label = "Toggle menu"
64+ aria-expanded = { isMobileMenuOpen }
65+ >
66+ { isMobileMenuOpen ? < X className = "w-5 h-5" /> : < Menu className = "w-5 h-5" /> }
67+ </ button >
68+
69+ { /* Logo */ }
70+ < h1 className = "text-xl font-bold text-gray-900 dark:text-white" >
71+ QuantResearch
72+ </ h1 >
73+ </ div >
74+
75+ { /* Search Bar - Hidden on mobile, visible on desktop */ }
76+ < div ref = { searchRef } className = "hidden lg:block flex-1 max-w-2xl mx-8" >
77+ < div className = "relative" >
78+ < Search className = "absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
79+ < input
80+ type = "text"
81+ value = { searchQuery }
82+ onChange = { ( e ) => {
83+ setSearchQuery ( e . target . value ) ;
84+ setShowSuggestions ( true ) ;
85+ } }
86+ onFocus = { ( ) => setShowSuggestions ( true ) }
87+ placeholder = "Search strategies, assets, or metrics..."
88+ className = "w-full pl-10 pr-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200 bg-gray-50 dark:bg-gray-800 hover:bg-white dark:hover:bg-gray-700 text-gray-900 dark:text-white placeholder-gray-500 dark:placeholder-gray-400"
89+ />
90+
91+ { /* Search Suggestions Dropdown */ }
92+ { showSuggestions && searchQuery && (
93+ < div className = "absolute top-full left-0 right-0 mt-1 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-lg z-50 max-h-60 overflow-y-auto" >
94+ < div className = "py-2" >
95+ { filteredSuggestions . length > 0 ? (
96+ filteredSuggestions . map ( ( suggestion , index ) => (
97+ < button
98+ key = { `suggestion-${ index } ` }
99+ onClick = { ( ) => {
100+ setSearchQuery ( suggestion ) ;
101+ setShowSuggestions ( false ) ;
102+ } }
103+ className = "w-full px-4 py-2 text-left text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
104+ >
105+ { suggestion }
106+ </ button >
107+ ) )
108+ ) : (
109+ < div className = "px-4 py-2 text-sm text-gray-500 dark:text-gray-400" >
110+ No results found
111+ </ div >
112+ ) }
113+ </ div >
114+ </ div >
115+ ) }
116+ </ div >
117+ </ div >
118+
119+ { /* Mobile Search Button */ }
120+ < button
121+ onClick = { handleMobileSearch }
122+ className = "lg:hidden p-2 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
123+ aria-label = "Search"
124+ >
125+ < Search className = "w-5 h-5" />
126+ </ button >
127+
128+ { /* Right Section - Notifications & Profile */ }
129+ < div className = "flex items-center space-x-4" >
130+ { /* Notifications - Hidden on mobile */ }
131+ < button
132+ className = "hidden sm:flex relative p-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
133+ aria-label = "Notifications"
134+ >
135+ < Bell className = "w-5 h-5" />
136+ < span className = "absolute -top-1 -right-1 w-4 h-4 bg-red-500 text-white text-xs rounded-full flex items-center justify-center" >
137+ 3
138+ </ span >
139+ </ button >
140+
141+ { /* Profile Dropdown */ }
142+ < div ref = { profileRef } className = "relative" >
143+ < button
144+ onClick = { ( ) => setIsProfileOpen ( ! isProfileOpen ) }
145+ className = "flex items-center space-x-3 p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
146+ aria-label = "User menu"
147+ aria-expanded = { isProfileOpen }
148+ >
149+ < div className = "w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center shadow-sm" >
150+ < User className = "w-4 h-4 text-white" />
151+ </ div >
152+ < div className = "hidden sm:flex items-center space-x-1" >
153+ < span className = "text-sm font-medium text-gray-700 dark:text-gray-300" >
154+ Sarah Wilson
155+ </ span >
156+ < ChevronDown
157+ className = { `w-4 h-4 text-gray-500 dark:text-gray-400 transition-transform duration-200 ${
158+ isProfileOpen ? 'rotate-180' : ''
159+ } `}
160+ />
161+ </ div >
162+ </ button >
163+
164+ { /* Dropdown Menu */ }
165+ { isProfileOpen && (
166+ < div className = "absolute right-0 top-full mt-2 w-48 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1 z-50" >
167+ < div className = "py-1" >
168+ { /* Mobile-only user name */ }
169+ < div className = "px-4 py-2 border-b border-gray-200 dark:border-gray-700 sm:hidden" >
170+ < span className = "text-sm font-medium text-gray-700 dark:text-gray-300" >
171+ Sarah Wilson
172+ </ span >
173+ </ div >
174+
175+ < button
176+ onClick = { ( ) => handleProfileAction ( 'Profile' ) }
177+ className = "flex items-center w-full px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-150"
178+ >
179+ < User className = "w-4 h-4 mr-3" />
180+ Profile
181+ </ button >
182+
183+ < button
184+ onClick = { ( ) => handleProfileAction ( 'Settings' ) }
185+ className = "flex items-center w-full px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-150"
186+ >
187+ < Settings className = "w-4 h-4 mr-3" />
188+ Settings
189+ </ button >
190+
191+ < div className = "border-t border-gray-200 dark:border-gray-700 my-1" />
192+
193+ < button
194+ onClick = { ( ) => handleProfileAction ( 'Sign Out' ) }
195+ className = "flex items-center w-full px-4 py-2 text-sm text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors duration-150"
196+ >
197+ < LogOut className = "w-4 h-4 mr-3" />
198+ Sign Out
199+ </ button >
200+ </ div >
201+ </ div >
202+ ) }
203+ </ div >
204+ </ div >
205+ </ div >
206+ </ header >
207+ ) ;
208+ } ;
0 commit comments