diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 2876494..8aa4d97 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -1,17 +1,85 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Search, Bell, Menu, Sparkles, User, Calendar, ShieldCheck, LogIn } from 'lucide-react'; +import { + Search, + Bell, + Menu, + Calendar, + LogIn, + Check, + X, + BookOpen, + Bus, + Users, + Calendar as ClubIcon, + Code2, + Trophy, + Bot, + Rocket, + ChevronRight +} from 'lucide-react'; import { useAuth } from '@/context/AuthContext'; +import { BUS_ROUTES, FACULTY_DATA, CLUBS_DATA } from '@/constants'; interface HeaderProps { onToggleSidebar: () => void; } +interface NotificationItem { + id: string; + title: string; + message: string; + time: string; + read: boolean; + type: 'academic' | 'dev' | 'campus' | 'bus'; +} + export default function Header({ onToggleSidebar }: HeaderProps) { const [searchQuery, setSearchQuery] = useState(''); + const [isSearchOpen, setIsSearchOpen] = useState(false); + const [isNotifOpen, setIsNotifOpen] = useState(false); const [currentDate, setCurrentDate] = useState(''); const navigate = useNavigate(); const { user, isAuthenticated, isVerifiedStudent, loginWithGoogle } = useAuth(); + const notifRef = useRef(null); + const searchRef = useRef(null); + + const [notifications, setNotifications] = useState([ + { + id: '1', + title: 'Moodle Notes & PYQs Updated', + message: '1st to 8th Semester notes and previous year question papers are published on Moodle.', + time: '10m ago', + read: false, + type: 'academic', + }, + { + id: '2', + title: 'Dev Collab Hub Open', + message: 'Post open-source project ideas or find co-developers across departments.', + time: '1h ago', + read: false, + type: 'dev', + }, + { + id: '3', + title: 'LeetCode Campus Rankings Live', + message: 'Monthly competitive programming leaderboard has been updated.', + time: '3h ago', + read: false, + type: 'academic', + }, + { + id: '4', + title: 'Campus Bus Schedule Confirmed', + message: 'All 4 campus bus routes operating on 7:00 AM morning schedule.', + time: '1d ago', + read: true, + type: 'bus', + }, + ]); + + const unreadCount = notifications.filter((n) => !n.read).length; useEffect(() => { const now = new Date(); @@ -24,33 +92,95 @@ export default function Header({ onToggleSidebar }: HeaderProps) { setCurrentDate(formatted); }, []); - const handleSearch = (e: React.FormEvent) => { - e.preventDefault(); - if (!searchQuery.trim()) return; + // Listen for ⌘K shortcut + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + setIsSearchOpen((prev) => !prev); + } + }; + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, []); + + // Close dropdowns on outside click + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (notifRef.current && !notifRef.current.contains(e.target as Node)) { + setIsNotifOpen(false); + } + if (searchRef.current && !searchRef.current.contains(e.target as Node)) { + setIsSearchOpen(false); + } + }; + document.addEventListener('mousedown', handleClickOutside); + return () => document.removeEventListener('mousedown', handleClickOutside); + }, []); + + const markAllAsRead = () => { + setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); + }; + + const clearNotifications = () => { + setNotifications([]); + }; + + // Search indexing & results calculation + const searchResults = (() => { + if (!searchQuery.trim()) return []; const q = searchQuery.toLowerCase(); - if (q.includes('note') || q.includes('pyq') || q.includes('syllabus')) { - navigate('/notes'); - } else if (q.includes('bus') || q.includes('route')) { - navigate('/bus-routes'); - } else if (q.includes('faculty') || q.includes('teacher') || q.includes('prof')) { - navigate('/faculty'); - } else if (q.includes('club') || q.includes('event')) { - navigate('/events'); - } else if (q.includes('ai') || q.includes('chat') || q.includes('bot')) { - navigate('/ai-assistant'); - } else if (q.includes('community') || q.includes('qa') || q.includes('question')) { - navigate('/community'); - } else if (q.includes('collab') || q.includes('dev')) { - navigate('/collab'); - } else if (q.includes('raise') || q.includes('incubator') || q.includes('pitch')) { - navigate('/raise'); - } else { - navigate(`/notes?search=${encodeURIComponent(searchQuery)}`); + const results: { title: string; desc: string; path: string; icon: any; category: string }[] = []; + + // Check modules + if ('notes pyqs moodle syllabus question paper'.includes(q)) { + results.push({ title: 'Academic Notes & PYQs', desc: 'Semester course notes & past papers', path: '/notes', icon: BookOpen, category: 'Academics' }); } + if ('bus route timing transport travel pickup'.includes(q)) { + results.push({ title: 'Campus Bus Routes', desc: 'Schedules and stops for all campus buses', path: '/bus-routes', icon: Bus, category: 'Utilities' }); + } + if ('chatbot assistant question help contact rules'.includes(q)) { + results.push({ title: 'Campus Chatbot', desc: 'Instant Q&A about campus life & rules', path: '/ai-assistant', icon: Bot, category: 'Academics' }); + } + if ('collab dev project developer team coding github'.includes(q)) { + results.push({ title: 'Dev Collab Hub', desc: 'Find co-developers & build projects', path: '/collab', icon: Code2, category: 'Dev' }); + } + if ('leetcode ranking leaderboard coding rank'.includes(q)) { + results.push({ title: 'LeetCode Leaderboard', desc: 'Campus competitive coding rankings', path: '/leetcode', icon: Trophy, category: 'Dev' }); + } + + // Search Bus Routes + BUS_ROUTES.forEach((r) => { + if (r.name.toLowerCase().includes(q) || r.number.toLowerCase().includes(q) || r.from.toLowerCase().includes(q)) { + results.push({ title: `${r.number}: ${r.name}`, desc: `Departure: ${r.departureTime} from ${r.from}`, path: '/bus-routes', icon: Bus, category: 'Bus Route' }); + } + }); + + // Search Faculty + FACULTY_DATA.forEach((f) => { + if (f.name.toLowerCase().includes(q) || f.department.toLowerCase().includes(q) || (f.specialization && f.specialization.toLowerCase().includes(q))) { + results.push({ title: f.name, desc: `${f.designation} — ${f.department}`, path: '/faculty', icon: Users, category: 'Faculty' }); + } + }); + + // Search Clubs + CLUBS_DATA.forEach((c) => { + if (c.name.toLowerCase().includes(q) || c.description.toLowerCase().includes(q)) { + results.push({ title: c.name, desc: c.description, path: '/events', icon: ClubIcon, category: 'Club' }); + } + }); + + return results.slice(0, 6); + })(); + + const handleSelectResult = (path: string) => { + setIsSearchOpen(false); + setSearchQuery(''); + navigate(path); }; return ( -
+
{/* Mobile Toggle */} - {/* Global Search Bar */} -
- - setSearchQuery(e.target.value)} - placeholder="Search notes, faculty, bus routes, clubs..." - className="w-full pl-10 pr-12 py-2 text-xs font-medium bg-slate-100/90 hover:bg-slate-100 focus:bg-white border border-transparent focus:border-purple-500/40 rounded-xl text-slate-800 placeholder-slate-400 outline-none transition-all shadow-inner" - style={{ fontFamily: 'Inter, sans-serif' }} - /> - - ⌘K - - + {/* Global Interactive Search Input Container */} +
+
setIsSearchOpen(true)} + className="relative w-64 sm:w-80 md:w-96 cursor-text" + > + + { + setSearchQuery(e.target.value); + setIsSearchOpen(true); + }} + onFocus={() => setIsSearchOpen(true)} + placeholder="Search notes, faculty, bus routes, clubs..." + className="w-full pl-10 pr-12 py-2 text-xs font-medium bg-slate-100/90 hover:bg-slate-100 focus:bg-white border border-transparent focus:border-indigo-500/40 rounded-xl text-slate-800 placeholder-slate-400 outline-none transition-all shadow-inner" + style={{ fontFamily: 'Inter, sans-serif' }} + /> + + ⌘K + +
+ + {/* Search Live Results Dropdown Overlay */} + {isSearchOpen && ( +
+
+ + {searchQuery.trim() ? 'Search Results' : 'Quick Navigation'} + + +
+ +
+ {searchResults.length > 0 ? ( + searchResults.map((res, i) => { + const Icon = res.icon; + return ( +
handleSelectResult(res.path)} + className="flex items-center justify-between p-2.5 rounded-xl hover:bg-indigo-50/70 cursor-pointer transition-colors group" + > +
+
+ +
+
+ + {res.title} + + {res.desc} +
+
+ + {res.category} + +
+ ); + }) + ) : ( +
+ + {searchQuery.trim() ? `No matches found for "${searchQuery}"` : 'Type to search notes, faculty, routes & clubs'} + +
+ + + +
+
+ )} +
+
+ )} +
{/* Right Header Status Bar */}
{/* Date Display Badge */}
- + {currentDate}
- {/* Notifications Icon */} - + {/* Functional Notifications Dropdown */} +
+ + + {/* Notifications Panel Dropdown */} + {isNotifOpen && ( +
+
+
+ + Notifications + + {unreadCount > 0 && ( + + {unreadCount} new + + )} +
+ +
+ {unreadCount > 0 && ( + + )} + {notifications.length > 0 && ( + + )} +
+
+ +
+ {notifications.length > 0 ? ( + notifications.map((n) => ( +
+
+ + {n.title} + + {n.time} +
+

+ {n.message} +

+
+ )) + ) : ( +
+ No new notifications +
+ )} +
+
+ )} +
{/* User Auth Quick Badge */} {isAuthenticated && user ? ( @@ -102,11 +383,11 @@ export default function Header({ onToggleSidebar }: HeaderProps) { {user.name} ) : ( -
+
{user.name.charAt(0)}
)} @@ -114,7 +395,7 @@ export default function Header({ onToggleSidebar }: HeaderProps) { {user.name} - + {isVerifiedStudent ? 'Verified Student' : 'RIT Account'}
@@ -122,7 +403,7 @@ export default function Header({ onToggleSidebar }: HeaderProps) { ) : ( + + {/* Desktop Collapse Toggle Button */} +
- {/* Quick Pitch CTA inside Sidebar */} - -
-
- + {/* Quick Dev Collab Launch CTA inside Sidebar (Expanded only) */} + {!isCollapsed && ( + +
+
+ +
+
+ + Dev Collab Hub + + Co-Developer Projects +
-
- - RAISE Incubator - - Pitch Startup Idea -
-
- - + + + )} {/* Navigation Groups */} -