Skip to content

Commit 92b6ddb

Browse files
committed
Refactor Dashboard Layout and Enhance Mobile Experience
- Removed the SearchBar component and integrated mobile sidebar functionality. - Added mobile sidebar with user profile and navigation links. - Improved responsiveness of UserProfileSection for mobile devices. - Updated ProgressCard to enhance stats grid layout for better mobile display. - Modified Hero component to reflect upcoming event details. - Enhanced CSS for mobile touch support, scrolling, and responsive grid layouts.
1 parent 80455d0 commit 92b6ddb

File tree

5 files changed

+343
-174
lines changed

5 files changed

+343
-174
lines changed

src/components/dashboard/DashboardLayout.jsx

Lines changed: 120 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,24 @@
11
import React, { useState, useEffect } from 'react';
22
import { Link, Outlet } from 'react-router-dom';
33
import { 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';
55
import UserProfileSection from './UserProfileSection';
66
import ProgressService from '../../utils/progressService';
77
import OptimizedDashboardService from '../../utils/optimizedDashboardService';
88
import { 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

7310
const 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

src/components/dashboard/ProgressCard.jsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,18 +101,18 @@ const ProgressCard = ({ enrollment, onClick }) => {
101101
</div>
102102
</div>
103103

104-
{/* Stats Grid */}
105-
<div className="grid grid-cols-3 gap-4 text-center mb-4">
106-
<div>
107-
<div className="text-lg font-bold text-green-600">{completedSections}</div>
104+
{/* Stats Grid - Responsive layout */}
105+
<div className="grid grid-cols-3 gap-2 sm:gap-4 text-center mb-4">
106+
<div className="p-2 rounded-lg bg-green-50">
107+
<div className="text-base sm:text-lg font-bold text-green-600">{completedSections}</div>
108108
<div className="text-xs text-gray-500">Completed</div>
109109
</div>
110-
<div>
111-
<div className="text-lg font-bold text-blue-600">{totalSections - completedSections}</div>
110+
<div className="p-2 rounded-lg bg-blue-50">
111+
<div className="text-base sm:text-lg font-bold text-blue-600">{totalSections - completedSections}</div>
112112
<div className="text-xs text-gray-500">Remaining</div>
113113
</div>
114-
<div>
115-
<div className="text-lg font-bold text-gray-900">{totalSections}</div>
114+
<div className="p-2 rounded-lg bg-gray-50">
115+
<div className="text-base sm:text-lg font-bold text-gray-900">{totalSections}</div>
116116
<div className="text-xs text-gray-500">Total</div>
117117
</div>
118118
</div>

0 commit comments

Comments
 (0)