Spaces:
Build error
Build error
File size: 10,452 Bytes
0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 caae15f 0702eb8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
"use client";
import Link from 'next/link';
import Image from 'next/image';
import { Home, InfoIcon, MessageCircle, Search, FileQuestion, Menu, X } from 'lucide-react';
import { usePathname } from 'next/navigation';
import { useTheme } from "next-themes";
import { useEffect, useState, useRef } from "react";
import { useMedia } from 'react-use';
import useSWR from 'swr'
import logo from '../../public/smart-retrieval-logo.webp'
interface NavLinkProps {
href: string;
children: React.ReactNode;
onClick?: () => void; // Include onClick as an optional prop
}
interface MobileMenuProps {
isOpen: boolean;
onClose: () => void;
}
const MobileMenu: React.FC<MobileMenuProps> = ({ isOpen, onClose }) => {
const isLargeScreen = useMedia('(min-width: 1024px)', false);
const menuRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
const handleOutsideClick = (event: MouseEvent | TouchEvent) => {
if (
!isLargeScreen &&
isOpen &&
!menuRef.current?.contains(event.target as Node) &&
!((event.target as HTMLElement).closest('.toggle-button')) // Exclude the toggle button
) {
onClose(); // Close the menu
}
};
if (!isLargeScreen && isOpen) {
// Add event listeners for both mouse and touch events
document.addEventListener('mousedown', handleOutsideClick);
}
return () => {
// Remove the event listener when the component unmounts
document.removeEventListener('mousedown', handleOutsideClick);
};
}, [isLargeScreen, isOpen, onClose]);
useEffect(() => {
if (isLargeScreen && isOpen) {
onClose();
}
}, [isLargeScreen, isOpen, onClose]);
return (
<div ref={menuRef} className={`w-full h-full p-2 bg-opacity-80 ${isOpen ? 'flex' : 'hidden'}`}>
<div className="flex items-center justify-center mt-2" style={{ width: '9%', height: '9%' }}>
<Image
className='rounded-full max-w-full'
src={logo}
alt="Logo"
style={{
width: 'auto',
height: 'auto',
}}
priority
sizes="100vw, 50vw, 33vw"
/>
</div>
<div className="flex items-center justify-center h-full">
{/* Mobile menu content */}
<div className="w-64 p-4 rounded-r-md">
<NavLink href="/" onClick={onClose}>
<div className="flex items-center mb-4">
<Home className="mr-2 h-5 w-5" />
Home
</div>
</NavLink>
<NavLink href="/about" onClick={onClose}>
<div className="flex items-center mb-4">
<InfoIcon className="mr-2 h-5 w-5" />
About
</div>
</NavLink>
<NavLink href="/chat" onClick={onClose}>
<div className="flex items-center mb-4">
<MessageCircle className="mr-2 h-5 w-5" />
Chat
</div>
</NavLink>
<NavLink href="/query" onClick={onClose}>
<div className="flex items-center mb-4">
<FileQuestion className="mr-2 h-5 w-5" />
Q&A
</div>
</NavLink>
<NavLink href="/search" onClick={onClose}>
<div className="flex items-center">
<Search className="mr-2 h-5 w-5" />
Search
</div>
</NavLink>
</div>
</div>
</div>
);
};
const NavLink: React.FC<NavLinkProps> = ({ href, children, onClick }) => {
// Use the useRouter hook to get information about the current route
const pathname = usePathname();
// Determine if the current tab is active
const isActive = pathname === href;
const handleClick = () => {
if (onClick) {
onClick(); // Call the onClick handler if provided
}
};
return (
<Link href={href} passHref>
{/* Add a class to highlight the active tab */}
<div className={`flex items-center font-bold ${isActive ? 'text-blue-500' : ''}`} onClick={handleClick}>
{children}
</div>
</Link>
);
};
export default function Header() {
const isLargeScreen = useMedia('(min-width: 1024px)', false);
const [mounted, setMounted] = useState(false);
const { theme, setTheme } = useTheme();
// const [apiStatus, setApiStatus] = useState(false);
// Use SWR for API status fetching
const healthcheck_api = process.env.NEXT_PUBLIC_HEALTHCHECK_API;
const { data: apiStatus, error: apiError } = useSWR(healthcheck_api, async (url) => {
try {
// Fetch the data
const response = await fetch(url);
if (!response.ok) {
throw new Error(response.statusText || 'Unknown Error');
}
const data = await response.json();
return data;
} catch (error: any) {
console.error('Error fetching Backend API Status:', error.message);
throw error;
}
}, {
revalidateOnFocus: true, // Revalidate when the window gains focus
revalidateIfStale: true, // Revalidate if the data is stale
refreshInterval: 60000, // Revalidate every 60 seconds
});
if (apiError) {
console.error('[Header] Error fetching Backend API Status:', apiError.message);
}
useEffect(() => {
setMounted(true);
}, []);
const [isMobileMenuOpen, setMobileMenuOpen] = useState(false);
const toggleMobileMenu = () => {
// Handle the toggle click here
if (isMobileMenuOpen) {
// If the menu is open, close it
setMobileMenuOpen(false);
} else {
// If the menu is closed, open it
setMobileMenuOpen(true);
}
};
if (!mounted) return null;
return (
<div className="z-10 max-w-5xl w-full text-sm">
{/* Navigation Bar */}
<nav className="fixed left-0 top-0 w-full bg-gradient-to-b from-zinc-200 pb-2 pt-2 backdrop-blur-2xl dark:border-neutral-700 dark:bg-zinc-700/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-100 lg:p-4 lg:dark:bg-zinc-800/30 shadow-xl">
<div className="flex items-center flex-wrap lg:flex-nowrap px-4">
{isLargeScreen && (
<div className="flex items-center" style={{ width: '6%', height: 'auto' }}>
<Image
className='rounded-full max-w-full'
src={logo}
alt="Logo"
style={{
width: 'auto',
height: 'auto',
}}
priority
sizes="100vw, 50vw, 33vw"
/>
</div>
)}
<div className="flex items-center pr-2 pl-2 gap-2">
<span className="hidden lg:inline lg:text-lg font-nunito font-bold">Smart Retrieval</span>
<span className="hidden lg:inline lg:text-lg font-nunito">|</span>
</div>
<div className="flex items-center gap-4 lg:hidden">
{/* Toggle button for mobile menu */}
<button
className="flex items-center text-xl transition duration-300 ease-in-out transform hover:scale-125 toggle-button"
title="Toggle mobile menu"
onClick={toggleMobileMenu}
>
{isMobileMenuOpen ? (
<span role="img" aria-label="close icon">
<X/>
</span>
) : (
<span role="img" aria-label="menu icon">
<Menu />
</span>)}
</button>
</div>
{/* Mobile menu component */}
<MobileMenu isOpen={isMobileMenuOpen} onClose={() => setMobileMenuOpen(false)} />
<div className={`hidden items-center gap-4 lg:flex`}>
<NavLink href="/">
<div className="flex items-center transition duration-300 ease-in-out transform hover:scale-125">
<Home className="mr-1 h-4 w-4" />
Home
</div>
</NavLink>
<NavLink href="/about">
<div className="flex items-center transition duration-300 ease-in-out transform hover:scale-125">
<InfoIcon className="mr-1 h-4 w-4" />
About
</div>
</NavLink>
<NavLink href="/chat">
<div className="flex items-center transition duration-300 ease-in-out transform hover:scale-125">
<MessageCircle className="mr-1 h-4 w-4" />
Chat
</div>
</NavLink>
<NavLink href="/query">
<div className="flex items-center transition duration-300 ease-in-out transform hover:scale-125">
<FileQuestion className="mr-1 h-4 w-4" />
Q&A
</div>
</NavLink>
<NavLink href="/search">
<div className="flex items-center transition duration-300 ease-in-out transform hover:scale-125">
<Search className="mr-1 h-4 w-4" />
Search
</div>
</NavLink>
</div>
<div className="flex items-center ml-auto">
{/* Status Page Button/Indicator */}
<span className='flex items-center mr-1'>API:</span>
<NavLink href='/status'>
<div className="flex items-center mr-2 text-xl transition duration-300 ease-in-out transform hover:scale-125">
{apiError ? (
<span role="img" aria-label="red circle">
π΄
</span>
) : (
<span role="img" aria-label="green circle">
π’
</span>
)}
</div>
</NavLink>
<span className="lg:text-lg font-nunito">|</span>
{/* Toggle button with icon based on the theme */}
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
className="flex items-center ml-2 text-xl transition duration-300 ease-in-out transform hover:scale-125"
title={`Toggle between dark & light mode (Current mode: ${theme})`}>
{theme === 'light' ? (
<span role="img" aria-label="sun emoji">
βοΈ
</span>
) : (
<span role="img" aria-label="moon emoji">
π
</span>
)}
</button>
</div>
</div>
</nav>
</div>
);
}
|