11import React , { useState , useEffect } from 'react' ;
22import { Link , Outlet } from 'react-router-dom' ;
33import { useAuth } from '../../hooks/useAuth' ;
4- import { LogOut , Settings , ChevronDown , Search , Home , X } from 'lucide-react' ;
4+ import { LogOut , Settings , ChevronDown , Home , X , User } from 'lucide-react' ;
55import UserProfileSection from './UserProfileSection' ;
66import ProgressService from '../../utils/progressService' ;
77import OptimizedDashboardService from '../../utils/optimizedDashboardService' ;
88import { getUserAvatarUrl } from '../../utils/avatarService.jsx' ;
9- import { SearchProvider } from '../../context/SearchContext.jsx' ;
10-
11- // Search Bar Component
12- const SearchBar = ( ) => {
13- const [ searchTerm , setSearchTerm ] = useState ( '' ) ;
14- const [ isSearchActive , setIsSearchActive ] = useState ( false ) ;
15-
16- // Listen for clear search events
17- useEffect ( ( ) => {
18- const handleClearSearch = ( ) => {
19- setSearchTerm ( '' ) ;
20- setIsSearchActive ( false ) ;
21- window . dispatchEvent ( new CustomEvent ( 'dashboardSearch' , {
22- detail : { searchTerm : '' , isActive : false }
23- } ) ) ;
24- } ;
25-
26- window . addEventListener ( 'clearSearch' , handleClearSearch ) ;
27- return ( ) => window . removeEventListener ( 'clearSearch' , handleClearSearch ) ;
28- } , [ ] ) ;
29-
30- const handleSearchChange = ( e ) => {
31- const value = e . target . value ;
32- setSearchTerm ( value ) ;
33- setIsSearchActive ( value . length > 0 ) ;
34-
35- // Dispatch custom event to notify other components
36- window . dispatchEvent ( new CustomEvent ( 'dashboardSearch' , {
37- detail : { searchTerm : value , isActive : value . length > 0 }
38- } ) ) ;
39- } ;
40-
41- const clearSearch = ( ) => {
42- setSearchTerm ( '' ) ;
43- setIsSearchActive ( false ) ;
44- window . dispatchEvent ( new CustomEvent ( 'dashboardSearch' , {
45- detail : { searchTerm : '' , isActive : false }
46- } ) ) ;
47- } ;
48-
49- return (
50- < div className = "relative w-full" >
51- < div className = "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none" >
52- < Search className = "h-4 w-4 text-gray-400" />
53- </ div >
54- < input
55- type = "text"
56- placeholder = "Search leagues, assignments..."
57- value = { searchTerm }
58- onChange = { handleSearchChange }
59- className = "block w-full pl-10 pr-10 py-2 border border-gray-200 rounded-lg text-sm placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-[#FFDE59]/50 focus:border-[#FFDE59] bg-gray-50 focus:bg-white transition-all duration-200"
60- />
61- { isSearchActive && (
62- < button
63- onClick = { clearSearch }
64- className = "absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600"
65- >
66- < X className = "h-4 w-4" />
67- </ button >
68- ) }
69- </ div >
70- ) ;
71- } ;
729
7310const DashboardLayout = ( ) => {
7411 const { user } = useAuth ( ) ;
7512 const [ dashboardData , setDashboardData ] = useState ( null ) ;
7613 const [ showUserMenu , setShowUserMenu ] = useState ( false ) ;
14+ const [ showMobileSidebar , setShowMobileSidebar ] = useState ( false ) ;
7715
7816 // Close dropdown when clicking outside
7917 useEffect ( ( ) => {
80- const handleClickOutside = ( ) => setShowUserMenu ( false ) ;
18+ const handleClickOutside = ( ) => {
19+ setShowUserMenu ( false ) ;
20+ setShowMobileSidebar ( false ) ;
21+ } ;
8122 document . addEventListener ( 'click' , handleClickOutside ) ;
8223 return ( ) => document . removeEventListener ( 'click' , handleClickOutside ) ;
8324 } , [ ] ) ;
@@ -103,8 +44,7 @@ const DashboardLayout = () => {
10344 } , [ user ] ) ;
10445
10546 return (
106- < SearchProvider >
107- < div className = "min-h-screen bg-gray-50" >
47+ < div className = "min-h-screen bg-gray-50" >
10848 { /* Simplified Header with Clean Design */ }
10949 < header className = "sticky top-0 z-50 backdrop-blur-md bg-white/90 border-b border-gray-200 shadow-sm" >
11050 { /* Simple gradient accent line */ }
@@ -124,7 +64,7 @@ const DashboardLayout = () => {
12464 { /* Subtle glow on hover */ }
12565 < div className = "absolute inset-0 rounded-lg bg-[#FFDE59]/10 opacity-0 group-hover:opacity-100 transition-opacity duration-200" > </ div >
12666 </ div >
127- < div className = "ml-3" >
67+ < div className = "ml-3 hidden sm:block " >
12868 < h1 className = "text-xl font-bold text-gray-900 group-hover:text-black transition-colors duration-200" >
12969 OpenLearn
13070 </ h1 >
@@ -133,26 +73,33 @@ const DashboardLayout = () => {
13373 </ Link >
13474 </ div >
13575
136- { /* Search Bar - Hidden on mobile */ }
137- < div className = "hidden md:flex flex-1 max-w-md mx-8" >
138- < SearchBar />
139- </ div >
76+ { /* Right Section */ }
77+ < div className = "flex items-center space-x-2 sm:space-x-4" >
78+ { /* Mobile Profile Button - Shows sidebar */ }
79+ < button
80+ onClick = { ( e ) => {
81+ e . stopPropagation ( ) ;
82+ setShowMobileSidebar ( ! showMobileSidebar ) ;
83+ } }
84+ className = "flex md:hidden items-center justify-center w-10 h-10 rounded-lg bg-gray-100/60 hover:bg-[#FFDE59]/15 transition-all duration-300 group"
85+ >
86+ < User className = "h-5 w-5 text-gray-600 group-hover:text-gray-900" />
87+ </ button >
14088
141- { /* Simplified Right Section */ }
142- < div className = "flex items-center space-x-4" >
14389 { /* Enhanced Admin Panel Link */ }
14490 { ( user ?. role === 'GRAND_PATHFINDER' || user ?. role === 'CHIEF_PATHFINDER' ) && (
14591 < Link
14692 to = "/admin"
14793 className = "hidden sm:flex items-center px-3 py-2 text-sm font-medium text-gray-700 hover:text-gray-900 bg-gray-100/60 hover:bg-[#FFDE59]/15 rounded-lg transition-all duration-300 hover:scale-105 border border-gray-200/50 hover:border-[#FFDE59]/40 group"
14894 >
14995 < Settings className = "h-4 w-4 mr-2 group-hover:rotate-45 transition-transform duration-300" />
150- Admin Panel
96+ < span className = "hidden lg:inline" > Admin Panel</ span >
97+ < span className = "lg:hidden" > Admin</ span >
15198 </ Link >
15299 ) }
153100
154- { /* Simplified User Menu */ }
155- < div className = "relative" >
101+ { /* Desktop User Menu */ }
102+ < div className = "hidden md:block relative" >
156103 < button
157104 onClick = { ( e ) => {
158105 e . stopPropagation ( ) ;
@@ -170,7 +117,7 @@ const DashboardLayout = () => {
170117 { /* Simplified status indicator */ }
171118 < div className = "absolute -bottom-0.5 -right-0.5 h-3 w-3 bg-green-500 rounded-full border-2 border-white" > </ div >
172119 </ div >
173- < div className = "hidden sm :block text-left" >
120+ < div className = "hidden lg :block text-left" >
174121 < p className = "text-sm font-semibold text-gray-900 truncate max-w-[120px] group-hover:text-black transition-colors duration-200" >
175122 { user ?. name || 'User' }
176123 </ p >
@@ -182,7 +129,7 @@ const DashboardLayout = () => {
182129 </ div >
183130 </ button >
184131
185- { /* Simplified Dropdown Menu */ }
132+ { /* Desktop Dropdown Menu */ }
186133 { showUserMenu && (
187134 < div className = "absolute right-0 mt-2 w-64 bg-white rounded-xl shadow-lg border border-gray-200 py-2 z-50 animate-fadeIn" >
188135 { /* Simple decorative accent */ }
@@ -249,26 +196,108 @@ const DashboardLayout = () => {
249196 </ div >
250197 </ header >
251198
252- { /* Main Content */ }
253- < main className = "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6" >
254- < div className = "grid grid-cols-1 lg:grid-cols-4 gap-6 min-h-[calc(100vh-120px)]" >
255- { /* Left Section - 25% Width (User Profile) */ }
256- < div className = "lg:col-span-1 h-full" >
257- < div className = "sticky top-24 h-[calc(100vh-140px)] overflow-y-auto dashboard-scroll" >
258- < UserProfileSection user = { user } dashboardData = { dashboardData } />
199+ { /* Mobile Sidebar Overlay */ }
200+ { showMobileSidebar && (
201+ < div className = "md:hidden fixed inset-0 z-50 overflow-hidden" >
202+ { /* Backdrop */ }
203+ < div className = "absolute inset-0 bg-black/40 backdrop-blur-sm" onClick = { ( ) => setShowMobileSidebar ( false ) } />
204+
205+ { /* Sidebar */ }
206+ < div className = "absolute left-0 top-0 h-full w-80 max-w-[85vw] bg-white shadow-xl transform transition-transform duration-300 ease-out" >
207+ { /* Sidebar Header */ }
208+ < div className = "flex items-center justify-between p-4 border-b border-gray-100 bg-gradient-to-r from-[#FFDE59]/10 to-[#FFD700]/10" >
209+ < div className = "flex items-center space-x-3" >
210+ < img
211+ src = { getUserAvatarUrl ( user , 'avataaars' , 40 ) }
212+ alt = { `${ user ?. name } avatar` }
213+ className = "h-10 w-10 rounded-full ring-2 ring-[#FFDE59]/30"
214+ />
215+ < div >
216+ < p className = "text-sm font-semibold text-gray-900 truncate max-w-[180px]" >
217+ { user ?. name || 'User' }
218+ </ p >
219+ < p className = "text-xs text-gray-500 capitalize" >
220+ { user ?. role ?. toLowerCase ( ) . replace ( '_' , ' ' ) || 'Student' }
221+ </ p >
222+ </ div >
223+ </ div >
224+ < button
225+ onClick = { ( ) => setShowMobileSidebar ( false ) }
226+ className = "p-2 hover:bg-gray-100 rounded-lg transition-colors"
227+ >
228+ < X className = "h-5 w-5 text-gray-500" />
229+ </ button >
230+ </ div >
231+
232+ { /* Sidebar Content */ }
233+ < div className = "flex-1 overflow-y-auto p-4" >
234+ < UserProfileSection user = { user } dashboardData = { dashboardData } isMobile = { true } />
235+ </ div >
236+
237+ { /* Sidebar Footer */ }
238+ < div className = "border-t border-gray-100 p-4 space-y-2" >
239+ < Link
240+ to = "/"
241+ className = "flex items-center px-3 py-2 text-sm text-gray-700 hover:bg-[#FFDE59]/10 hover:text-gray-900 transition-all duration-200 rounded-lg group"
242+ onClick = { ( ) => setShowMobileSidebar ( false ) }
243+ >
244+ < Home className = "h-4 w-4 mr-3" />
245+ Home
246+ </ Link >
247+
248+ { ( user ?. role === 'GRAND_PATHFINDER' || user ?. role === 'CHIEF_PATHFINDER' ) && (
249+ < Link
250+ to = "/admin"
251+ className = "flex items-center px-3 py-2 text-sm text-gray-700 hover:bg-[#FFDE59]/10 hover:text-gray-900 transition-all duration-200 rounded-lg group"
252+ onClick = { ( ) => setShowMobileSidebar ( false ) }
253+ >
254+ < Settings className = "h-4 w-4 mr-3" />
255+ Admin Panel
256+ </ Link >
257+ ) }
258+
259+ < Link
260+ to = "/logout"
261+ className = "flex items-center px-3 py-2 text-sm text-red-600 hover:bg-red-50 hover:text-red-700 transition-all duration-200 rounded-lg"
262+ onClick = { ( ) => setShowMobileSidebar ( false ) }
263+ >
264+ < LogOut className = "h-4 w-4 mr-3" />
265+ Sign out
266+ </ Link >
259267 </ div >
260268 </ div >
261-
262- { /* Right Section - 75% Width (Main Content) */ }
263- < div className = "lg:col-span-3 h-full" >
264- < div className = "h-[calc(100vh-140px)] overflow-y-auto dashboard-scroll" >
269+ </ div >
270+ ) }
271+
272+ { /* Main Content - Clean Responsive Layout */ }
273+ < main className = "flex-1" >
274+ < div className = "max-w-7xl mx-auto" >
275+ { /* Desktop Layout: Sidebar + Content */ }
276+ < div className = "hidden md:flex gap-6 p-6" >
277+ { /* Desktop Sidebar - Clean fixed width */ }
278+ < div className = "w-80 flex-shrink-0" >
279+ < div className = "sticky top-24 max-h-[calc(100vh-140px)] overflow-y-auto dashboard-scroll" >
280+ < UserProfileSection user = { user } dashboardData = { dashboardData } isMobile = { false } />
281+ </ div >
282+ </ div >
283+
284+ { /* Desktop Main Content - Flexible width */ }
285+ < div className = "flex-1 min-w-0" >
286+ < div className = "max-h-[calc(100vh-140px)] overflow-y-auto dashboard-scroll" >
287+ < Outlet />
288+ </ div >
289+ </ div >
290+ </ div >
291+
292+ { /* Mobile Layout: Clean Full Width */ }
293+ < div className = "md:hidden" >
294+ < div className = "px-4 py-6" >
265295 < Outlet />
266296 </ div >
267297 </ div >
268298 </ div >
269299 </ main >
270300 </ div >
271- </ SearchProvider >
272301 ) ;
273302} ;
274303
0 commit comments