Spaces:
Running
Running
import { useLocation, useNavigate } from "react-router-dom"; | |
import { Button, PageContainer, Container } from "@ifrc-go/ui"; | |
import { | |
UploadCloudLineIcon, | |
AnalysisIcon, | |
SearchLineIcon, | |
QuestionLineIcon, | |
GoMainIcon, | |
SettingsIcon, | |
ChevronDownLineIcon, | |
MenuLineIcon, | |
} from "@ifrc-go/icons"; | |
import { useState, useEffect, useRef } from "react"; | |
import styles from './HeaderNav.module.css'; | |
declare global { | |
interface Window { | |
confirmNavigationIfNeeded?: (to: string) => void; | |
} | |
} | |
const navItems = [ | |
{ to: "/upload", label: "Upload", Icon: UploadCloudLineIcon }, | |
{ to: "/explore", label: "Explore", Icon: SearchLineIcon }, | |
{ to: "/analytics", label: "Analytics", Icon: AnalysisIcon }, | |
]; | |
export default function HeaderNav() { | |
const location = useLocation(); | |
const navigate = useNavigate(); | |
const [isDropdownOpen, setIsDropdownOpen] = useState(false); | |
const dropdownRef = useRef<HTMLDivElement>(null); | |
// Close dropdown when clicking outside | |
useEffect(() => { | |
const handleClickOutside = (event: MouseEvent) => { | |
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { | |
setIsDropdownOpen(false); | |
} | |
}; | |
document.addEventListener('mousedown', handleClickOutside); | |
return () => { | |
document.removeEventListener('mousedown', handleClickOutside); | |
}; | |
}, []); | |
return ( | |
<nav className="border-b border-gray-200 bg-white shadow-sm sticky top-0 z-50 backdrop-blur-sm bg-white/95"> | |
<PageContainer | |
className="border-b-2 border-ifrcRed" | |
contentClassName="grid grid-cols-3 items-center py-6" | |
> | |
<div | |
className="flex items-center gap-4 min-w-0 cursor-pointer group transition-all duration-200 hover:scale-105" | |
onClick={() => { | |
// Prevent navigation to home when already on upload page | |
if (location.pathname === "/upload" || location.pathname === "/") { | |
return; | |
} | |
if (location.pathname === "/upload") { | |
if (window.confirmNavigationIfNeeded) { | |
window.confirmNavigationIfNeeded('/'); | |
return; | |
} | |
if (!confirm("You have unsaved changes. Are you sure you want to leave?")) { | |
return; | |
} | |
} | |
navigate('/'); | |
}} | |
> | |
<div className="p-2 rounded-lg bg-gradient-to-br from-ifrcRed/10 to-ifrcRed/20 group-hover:from-ifrcRed/20 group-hover:to-ifrcRed/30 transition-all duration-200"> | |
<GoMainIcon className="h-8 w-8 flex-shrink-0 text-ifrcRed" /> | |
</div> | |
<div className="flex flex-col"> | |
<span className="font-bold text-xl text-gray-900 leading-tight">PromptAid Vision</span> | |
</div> | |
</div> | |
<div className="flex justify-center"> | |
<nav className="flex items-center space-x-4 bg-gray-50/80 rounded-xl p-2 backdrop-blur-sm"> | |
{navItems.map(({ to, label, Icon }) => { | |
const isActive = location.pathname === to || | |
(to === '/upload' && location.pathname === '/') || | |
(to === '/explore' && location.pathname.startsWith('/map/')); | |
const isUploadPage = location.pathname === "/upload" || location.pathname === "/"; | |
const isUploadOrHomeNav = to === "/upload" || to === "/"; | |
return ( | |
<div key={to} className="relative"> | |
<Container withInternalPadding className="p-2"> | |
<Button | |
name={label.toLowerCase()} | |
variant={isActive ? "primary" : "tertiary"} | |
size={1} | |
className={`transition-all duration-200 ${ | |
isActive | |
? 'shadow-lg shadow-ifrcRed/20 transform scale-105' | |
: 'hover:bg-white hover:shadow-md hover:scale-105' | |
}`} | |
onClick={() => { | |
if (isUploadPage && isUploadOrHomeNav) { | |
return; | |
} | |
if (location.pathname === "/upload") { | |
if (window.confirmNavigationIfNeeded) { | |
window.confirmNavigationIfNeeded(to); | |
return; | |
} | |
if (!confirm("You have unsaved changes. Are you sure you want to leave?")) { | |
return; | |
} | |
} | |
navigate(to); | |
}} | |
> | |
<Icon className={`w-4 h-4 transition-transform duration-200 ${ | |
isActive ? 'scale-110' : 'group-hover:scale-110' | |
}`} /> | |
<span className="inline ml-2 font-semibold">{label}</span> | |
</Button> | |
</Container> | |
{isActive && ( | |
<div className="absolute -bottom-2 left-1/2 transform -translate-x-1/2 w-8 h-1 bg-ifrcRed rounded-full animate-pulse"></div> | |
)} | |
</div> | |
); | |
})} | |
</nav> | |
</div> | |
<div className="flex justify-end"> | |
<div className={styles.dropdownContainer} ref={dropdownRef}> | |
<Container withInternalPadding className="p-2"> | |
<Button | |
name="more-options" | |
variant={isDropdownOpen ? "primary" : "tertiary"} | |
size={1} | |
className="transition-all duration-200" | |
onClick={() => setIsDropdownOpen(!isDropdownOpen)} | |
> | |
<MenuLineIcon className="w-4 h-4" /> | |
</Button> | |
</Container> | |
{/* Enhanced Dropdown Menu */} | |
{isDropdownOpen && ( | |
<div className={styles.dropdownMenu}> | |
<div className={styles.dropdownContent}> | |
<Container withInternalPadding className="p-2"> | |
<Button | |
name="help-support" | |
variant="tertiary" | |
size={1} | |
className="w-full justify-start" | |
onClick={() => { | |
setIsDropdownOpen(false); | |
if (location.pathname === "/upload") { | |
if (window.confirmNavigationIfNeeded) { | |
window.confirmNavigationIfNeeded('/help'); | |
return; | |
} | |
if (!confirm("You have unsaved changes. Are you sure you want to leave?")) { | |
return; | |
} | |
} | |
navigate('/help'); | |
}} | |
> | |
<QuestionLineIcon className="w-4 h-4" /> | |
<span className="ml-2 font-semibold">Help & Support</span> | |
</Button> | |
</Container> | |
<Container withInternalPadding className="p-2"> | |
<Button | |
name="dev" | |
variant="tertiary" | |
size={1} | |
className="w-full justify-start" | |
onClick={() => { | |
setIsDropdownOpen(false); | |
if (location.pathname === "/upload") { | |
if (window.confirmNavigationIfNeeded) { | |
window.confirmNavigationIfNeeded('/admin'); | |
return; | |
} | |
if (!confirm("You have unsaved changes. Are you sure you want to leave?")) { | |
return; | |
} | |
} | |
navigate('/admin'); | |
}} | |
> | |
<SettingsIcon className="w-4 h-4" /> | |
<span className="ml-2 font-semibold">Dev</span> | |
</Button> | |
</Container> | |
</div> | |
</div> | |
)} | |
</div> | |
</div> | |
</PageContainer> | |
</nav> | |
); | |
} | |