Files
membership-fe/src/components/PublicNavbar.js
Koncept Kit 467f34b42a - - New ThemeConfigContext provider that fetches theme on app load and applies it to the DOM (title, meta description, favicon, CSS variables,
theme-color)/- - Admin Theme settings page under Settings > Theme tab/- All logo references (5 components) now pull from the theme config with fallback to default
2026-01-27 21:32:22 +07:00

524 lines
22 KiB
JavaScript

import React, { useState } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { Button } from './ui/button';
import { useAuth } from '../context/AuthContext';
import { useThemeConfig } from '../context/ThemeConfigContext';
import { ChevronDown, Menu, X } from 'lucide-react';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from './ui/dropdown-menu';
const PublicNavbar = () => {
const { user, logout } = useAuth();
const { getLogoUrl } = useThemeConfig();
const navigate = useNavigate();
const location = useLocation();
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
// Helper function to check if a path is active
const isActive = (path) => {
if (path.includes('#')) {
// For hash links like /#welcome
return location.pathname + location.hash === path || location.hash === path.replace('/', '');
}
return location.pathname === path || location.pathname.startsWith(path + '/');
};
// Check if any About Us sub-page is active
const isAboutActive = () => {
return location.pathname.startsWith('/about');
};
// Get logo URL from theme config (with fallback to default)
const loafLogo = getLogoUrl();
const handleAuthAction = () => {
if (user) {
logout();
navigate('/');
} else {
navigate('/login');
}
};
// Active and inactive link styles for desktop
const getDesktopLinkClasses = (path) => {
const baseClasses = "text-[17.5px] font-medium transition-all px-3 py-1 rounded-md";
if (isActive(path)) {
return `${baseClasses} text-[var(--orange-light)] hover:text-[var(--orange-coral)] `;
}
return `${baseClasses} text-white hover:opacity-80`;
};
// Active and inactive link styles for mobile
const getMobileLinkClasses = (path) => {
const baseClasses = "text-base font-medium px-4 py-3 rounded-md transition-colors";
if (isActive(path)) {
return `${baseClasses} bg-[var(--orange-light)] hover:bg-[var(--orange-coral)] text-[var(--purple-deep)]`;
}
return `${baseClasses} text-white hover:bg-[var(--purple-deep)]`;
};
// Active and inactive link styles for mobile sub-items (About Us)
const getMobileSubLinkClasses = (path) => {
const baseClasses = "text-sm font-medium px-6 py-2 rounded-md transition-colors block";
if (isActive(path)) {
return `${baseClasses} bg-[var(--orange-light)] hover:bg-[var(--orange-coral)] text-[var(--purple-deep)]`;
}
return `${baseClasses} text-[var(--neutral-800)] hover:bg-[var(--purple-deep)] hover:text-white`;
};
return (
<>
{/* Top Header - Auth Actions */}
<div className='sticky top-0 inset-x-0 z-50'>
<header className="bg-gradient-to-r flex-wrap from-[var(--purple-amethyst)] to-[var(--purple-deep)] px-[20px] py-[10px] flex md:justify-end justify-between items-center gap-4 sm:gap-6">
<div className='flex gap-4 sm:gap-6 items-center'>
{user && (
<span
className="text-white text-base font-medium"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Welcome, {user.first_name}
</span>
)}
{(user?.role === 'admin' || user?.role === 'superadmin') && (
<Link
to="/admin"
className="text-white text-base font-medium hover:opacity-80 transition-opacity"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Dashboard
</Link>
)}
<button
onClick={handleAuthAction}
className="text-white text-base font-medium hover:opacity-80 transition-opacity bg-transparent border-none cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{user ? 'Logout' : 'Login'}
</button>
{!user && (
<Link
to="/register"
className="text-white text-base font-medium hover:opacity-80 transition-opacity"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Register
</Link>
)}
</div>
<Link to="/donate">
<Button
className="bg-[var(--orange-light)] hover:bg-[var(--orange-coral)] text-[var(--purple-deep)] rounded-[25px] px-[50px] py-[5px] text-[16.5px] font-semibold h-[41px]"
style={{ fontFamily: "'Montserrat', sans-serif" }}
>
Donate
</Button>
</Link>
</header>
{/* Main Header - Navigation */}
<header className=" bg-brand-purple px-[20px] py-2 flex justify-between items-center">
<Link to="/">
<img src={loafLogo} alt="LOAF Logo" className="h-16 w-16 sm:h-20 sm:w-20 md:h-28 md:w-28 object-contain" />
</Link>
{/* Mobile Menu Button */}
<button
onClick={() => setIsMobileMenuOpen(true)}
className="lg:hidden p-2 text-white hover:bg-[var(--purple-deep)] rounded-md transition-colors"
aria-label="Open menu"
>
<Menu className="size-14" />
</button>
{/* Desktop Navigation */}
<nav className="hidden lg:flex gap-6 items-center">
<Link
to="/#welcome"
className={getDesktopLinkClasses('/#welcome')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Welcome
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className={`${isAboutActive()
? "text-[var(--orange-light)] hover:text-[var(--orange-coral)]"
: "text-white hover:opacity-80"} text-[17.5px] font-medium transition-all flex items-center gap-1 bg-transparent border-none cursor-pointer px-3 py-1 rounded-md`}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
About Us
<ChevronDown className="h-4 w-4" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="bg-background min-w-[220px]">
<DropdownMenuItem asChild>
<Link to="/about/history" className="w-full px-3 py-2 text-[var(--purple-deep)] hover:bg-[var(--lavender-300)] cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
History
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link to="/about/mission-values" className="w-full px-3 py-2 text-[var(--purple-deep)] hover:bg-[var(--lavender-300)] cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Mission and Values
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link to="/about/board" className="w-full px-3 py-2 text-[var(--purple-deep)] hover:bg-[var(--lavender-300)] cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Board of Directors
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
<Link
to={user ? "/dashboard" : "/become-a-member"}
className={getDesktopLinkClasses(user ? "/dashboard" : "/become-a-member")}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{user ? 'My Profile' : 'Become a Member'}
</Link>
{!user && (
<Link
to="/login"
className={getDesktopLinkClasses('/login')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Members Only
</Link>
)}
{user && (
<>
<Link
to="/events"
className={getDesktopLinkClasses('/events')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Events
</Link>
<Link
to="/members/calendar"
className={getDesktopLinkClasses('/members/calendar')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Calendar
</Link>
<Link
to="/members/directory"
className={getDesktopLinkClasses('/members/directory')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Directory
</Link>
<Link
to="/members/gallery"
className={getDesktopLinkClasses('/members/gallery')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Gallery
</Link>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
className={`${location.pathname.startsWith('/members/newsletters') || location.pathname.startsWith('/members/financials') || location.pathname.startsWith('/members/bylaws')
? "text-[var(--orange-light)] hover:text-[var(--orange-coral)]"
: "text-white hover:opacity-80"} text-[17.5px] font-medium transition-all flex items-center gap-1 bg-transparent border-none cursor-pointer px-3 py-1 rounded-md`}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Documents
<ChevronDown className="h-4 w-4" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start" className="bg-background min-w-[220px]">
<DropdownMenuItem asChild>
<Link to="/members/newsletters" className="w-full px-3 py-2 text-[var(--purple-deep)] hover:bg-[var(--lavender-300)] cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Newsletters
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link to="/members/financials" className="w-full px-3 py-2 text-[var(--purple-deep)] hover:bg-[var(--lavender-300)] cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Financials
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link to="/members/bylaws" className="w-full px-3 py-2 text-[var(--purple-deep)] hover:bg-[var(--lavender-300)] cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Bylaws
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</>
)}
{/* <Link
to="/resources"
className={getDesktopLinkClasses('/resources')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Resources
</Link>
<Link
to="/contact-us"
className={getDesktopLinkClasses('/contact-us')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Contact Us
</Link> */}
</nav>
</header>
</div>
{/* Mobile Menu Drawer */}
{isMobileMenuOpen && (
<div className="fixed inset-0 z-50 lg:hidden">
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50 backdrop-blur-sm"
onClick={() => setIsMobileMenuOpen(false)}
/>
{/* Drawer */}
<div className="fixed right-0 top-0 h-full w-[280px] bg-brand-purple shadow-xl overflow-y-auto scrollbar-dashboard">
{/* Header */}
<div className="flex justify-between items-center p-6 border-b border-[var(--purple-deep)]">
<span className="text-white text-lg font-semibold" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Menu
</span>
<button
onClick={() => setIsMobileMenuOpen(false)}
className="p-2 text-white hover:bg-[var(--purple-deep)] rounded-md transition-colors"
aria-label="Close menu"
>
<X className="h-5 w-5" />
</button>
</div>
{/* User Info */}
{user && (
<div className="px-6 py-4 border-b border-[var(--purple-deep)]">
<p className="text-white text-sm opacity-90" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Welcome,
</p>
<p className="text-white font-semibold text-base" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{user.first_name} {user.last_name}
</p>
</div>
)}
{/* Navigation Links */}
<nav className="flex flex-col p-6 space-y-4">
<Link
to="/#welcome"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/#welcome')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Welcome
</Link>
{/* About Us Section */}
<div className="space-y-2">
<p
className={`text-base font-semibold px-4 py-2 rounded-md ${isAboutActive() ? 'text-[var(--orange-light)]' : 'text-white'}`}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
About Us
</p>
<Link
to="/about/history"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileSubLinkClasses('/about/history')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
History
</Link>
<Link
to="/about/mission-values"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileSubLinkClasses('/about/mission-values')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Mission and Values
</Link>
<Link
to="/about/board"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileSubLinkClasses('/about/board')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Board of Directors
</Link>
</div>
<Link
to={user ? "/dashboard" : "/become-a-member"}
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses(user ? "/dashboard" : "/become-a-member")}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{user ? 'My Profile' : 'Become a Member'}
</Link>
{!user && (
<Link
to="/login"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/login')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Members Only
</Link>
)}
{user && (
<>
<Link
to="/events"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/events')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Events
</Link>
<Link
to="/members/calendar"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/members/calendar')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Calendar
</Link>
<Link
to="/members/directory"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/members/directory')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Directory
</Link>
<Link
to="/members/gallery"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/members/gallery')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Gallery
</Link>
{/* Documents Section */}
<div className="space-y-2">
<p
className={`text-base font-semibold px-4 py-2 rounded-md ${location.pathname.startsWith('/members/newsletters') || location.pathname.startsWith('/members/financials') || location.pathname.startsWith('/members/bylaws') ? 'text-[var(--orange-light)]' : 'text-white'}`}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Documents
</p>
<Link
to="/members/newsletters"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileSubLinkClasses('/members/newsletters')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Newsletters
</Link>
<Link
to="/members/financials"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileSubLinkClasses('/members/financials')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Financials
</Link>
<Link
to="/members/bylaws"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileSubLinkClasses('/members/bylaws')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Bylaws
</Link>
</div>
</>
)}
<Link
to="/resources"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/resources')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Resources
</Link>
<Link
to="/contact-us"
onClick={() => setIsMobileMenuOpen(false)}
className={getMobileLinkClasses('/contact-us')}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Contact Us
</Link>
{/* Auth Actions */}
<div className="pt-4 border-t border-[var(--purple-deep)] space-y-2">
{(user?.role === 'admin' || user?.role === 'superadmin') && (
<Link
to="/admin"
onClick={() => setIsMobileMenuOpen(false)}
className="block text-white text-base font-medium hover:bg-[var(--purple-deep)] px-4 py-3 rounded-md transition-colors"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Dashboard
</Link>
)}
<button
onClick={() => {
handleAuthAction();
setIsMobileMenuOpen(false);
}}
className="w-full text-left text-white text-base font-medium hover:bg-[var(--purple-deep)] px-4 py-3 rounded-md transition-colors"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{user ? 'Logout' : 'Login'}
</button>
{!user && (
<Link
to="/register"
onClick={() => setIsMobileMenuOpen(false)}
className="block text-white text-base font-medium hover:bg-[var(--purple-deep)] px-4 py-3 rounded-md transition-colors"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Register
</Link>
)}
<Link
to="/donate"
onClick={() => setIsMobileMenuOpen(false)}
className="block w-full"
>
<Button className="w-full bg-[var(--orange-light)] hover:bg-[var(--orange-coral)] text-[var(--purple-deep)] rounded-[25px] px-6 py-3 text-base font-semibold">
Donate
</Button>
</Link>
</div>
</nav>
</div>
</div>
)}
</>
);
};
export default PublicNavbar;