diff --git a/public/campus-video.mp4 b/public/campus-video.mp4 new file mode 100644 index 0000000..7cbb7ff Binary files /dev/null and b/public/campus-video.mp4 differ diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx new file mode 100644 index 0000000..2876494 --- /dev/null +++ b/src/components/Header/Header.tsx @@ -0,0 +1,135 @@ +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Search, Bell, Menu, Sparkles, User, Calendar, ShieldCheck, LogIn } from 'lucide-react'; +import { useAuth } from '@/context/AuthContext'; + +interface HeaderProps { + onToggleSidebar: () => void; +} + +export default function Header({ onToggleSidebar }: HeaderProps) { + const [searchQuery, setSearchQuery] = useState(''); + const [currentDate, setCurrentDate] = useState(''); + const navigate = useNavigate(); + const { user, isAuthenticated, isVerifiedStudent, loginWithGoogle } = useAuth(); + + useEffect(() => { + const now = new Date(); + const formatted = now.toLocaleDateString('en-US', { + weekday: 'short', + month: 'short', + day: 'numeric', + year: 'numeric', + }); + setCurrentDate(formatted); + }, []); + + const handleSearch = (e: React.FormEvent) => { + e.preventDefault(); + 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)}`); + } + }; + + 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 + + +
+ + {/* Right Header Status Bar */} +
+ {/* Date Display Badge */} +
+ + {currentDate} +
+ + {/* Notifications Icon */} + + + {/* User Auth Quick Badge */} + {isAuthenticated && user ? ( +
+ {user.pictureUrl ? ( + {user.name} + ) : ( +
+ {user.name.charAt(0)} +
+ )} +
+ + {user.name} + + + {isVerifiedStudent ? 'Verified Student' : 'RIT Account'} + +
+
+ ) : ( + + )} +
+
+ ); +} diff --git a/src/components/Sidebar/Sidebar.tsx b/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..2fe921b --- /dev/null +++ b/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,281 @@ +import { useState, useEffect } from 'react'; +import { Link, useLocation, useNavigate } from 'react-router-dom'; +import { motion, AnimatePresence } from 'framer-motion'; +import { + LayoutDashboard, + BookOpen, + Wrench, + Bot, + MapPin, + Bus, + Users, + Calendar, + MessageSquare, + Code2, + Trophy, + Rocket, + ShieldAlert, + LogOut, + LogIn, + ShieldCheck, + ChevronRight, + Sparkles, + Menu, + X +} from 'lucide-react'; +import { useAuth } from '@/context/AuthContext'; + +interface SidebarProps { + isOpen: boolean; + onToggle: () => void; +} + +interface NavItem { + label: string; + path: string; + icon: any; + badge?: string; + highlight?: boolean; +} + +interface NavGroup { + title: string; + items: NavItem[]; +} + +export default function Sidebar({ isOpen, onToggle }: SidebarProps) { + const location = useLocation(); + const navigate = useNavigate(); + const { user, isAuthenticated, isVerifiedStudent, loginWithGoogle, logout } = useAuth(); + + const checkIsActive = (linkPath: string) => { + const [pathName, searchString] = linkPath.split('?'); + if (location.pathname !== pathName) return false; + + if (pathName === '/notes') { + const params = new URLSearchParams(location.search); + const hasToolkit = params.has('toolkit') || params.get('section') === 'toolkit'; + if (searchString) { + return hasToolkit; + } else { + return !hasToolkit; + } + } + + return true; + }; + + const navGroups: NavGroup[] = [ + { + title: 'ACADEMICS & PORTAL', + items: [ + { label: 'Dashboard', path: '/', icon: LayoutDashboard }, + { label: 'Notes & PYQs', path: '/notes', icon: BookOpen }, + { label: 'Student Toolkit', path: '/notes?section=toolkit', icon: Wrench }, + { label: 'RIT AI Chatbot', path: '/ai-assistant', icon: Bot, badge: 'AI' }, + ], + }, + { + title: 'CAMPUS & UTILITIES', + items: [ + { label: 'Bus Tracker', path: '/bus-routes', icon: Bus }, + { label: 'Campus Map', path: '/campus', icon: MapPin }, + { label: 'Faculty Directory', path: '/faculty', icon: Users }, + { label: 'Student Clubs', path: '/events', icon: Calendar }, + ], + }, + { + title: 'COMMUNITY & DEV', + items: [ + { label: 'Freshers Q&A', path: '/community', icon: MessageSquare }, + { label: 'Dev Collab Hub', path: '/collab', icon: Code2 }, + { label: 'LeetCode Board', path: '/leetcode', icon: Trophy }, + { label: 'RAISE Incubator', path: '/raise', icon: Rocket, highlight: true }, + ], + }, + ]; + + return ( + <> + {/* Mobile Backdrop Overlay */} + + {isOpen && ( + + )} + + + {/* Main Sidebar Container */} + + + ); +} diff --git a/src/layouts/MainLayout.tsx b/src/layouts/MainLayout.tsx index efe61ee..7e3f9f2 100644 --- a/src/layouts/MainLayout.tsx +++ b/src/layouts/MainLayout.tsx @@ -1,6 +1,8 @@ -import { Outlet, useSearchParams, useLocation } from 'react-router-dom'; +import { useState } from 'react'; +import { Outlet, useLocation } from 'react-router-dom'; import { motion, AnimatePresence } from 'framer-motion'; -import Navbar from '@/components/Navbar/Navbar'; +import Sidebar from '@/components/Sidebar/Sidebar'; +import Header from '@/components/Header/Header'; import Footer from '@/components/Footer/Footer'; const pageVariants = { @@ -10,26 +12,41 @@ const pageVariants = { }; export default function MainLayout() { + const [isSidebarOpen, setIsSidebarOpen] = useState(false); const location = useLocation(); - const [searchParams] = useSearchParams(); - const hideFooter = location.pathname === '/notes' && searchParams.has('toolkit'); + + const toggleSidebar = () => setIsSidebarOpen((prev) => !prev); return ( -
- - - - - - - {!hideFooter &&
); }