import { Link, useNavigate } from 'react-router-dom' import { useState, useEffect, useRef } from 'react' import { API_BASE, type Category, type StoreSettings } from '../types' import { ShoppingBag, Menu as MenuIcon, X, Heart, Globe, Check } from 'lucide-react' import { useCartStore } from '../store/cartStore' import { useWishlistStore } from '../store/wishlistStore' import { useAuthStore } from '../store/authStore' import { logoutLiff, loginLiff } from '../lib/liff' import { useTranslation } from 'react-i18next' import { useMediaQuery } from '../hooks/useMediaQuery' import { DesktopMegaMenu } from './DesktopMegaMenu' import { MobileDrawer } from './MobileDrawer' import { useMenuStore } from '../store/menuStore' interface NavbarProps { categories?: Category[] topOffsetClassName?: string } export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) { const isDesktop = useMediaQuery('(min-width: 768px)') const [searchOpen, setSearchOpen] = useState(false) const [mobileOpen, setMobileOpen] = useState(false) const [langSheetOpen, setLangSheetOpen] = useState(false) const [searchQuery, setSearchQuery] = useState('') const [scrolled, setScrolled] = useState(false) const [hasAnnouncement, setHasAnnouncement] = useState(false) const [settings, setSettings] = useState(null) const navigate = useNavigate() const { toggleCart, getCartCount } = useCartStore() const cartCount = getCartCount() const wishlistCount = useWishlistStore(state => state.items.length) const { customer } = useAuthStore() const { t, i18n } = useTranslation() const { megaMenu, fetchMegaMenu } = useMenuStore() const navRef = useRef(null) const LANGUAGES = [ { code: 'th', label: 'ไทย', short: 'TH' }, { code: 'en', label: 'English', short: 'EN' }, { code: 'ja', label: '日本語', short: 'JP' }, { code: 'zh', label: '简体中文', short: 'ZH' }, { code: 'vi', label: 'Tiếng Việt', short: 'VN' }, { code: 'ko', label: '한국어', short: 'KO' }, ] const changeLanguage = (code: string) => { i18n.changeLanguage(code) localStorage.setItem('homaroma_lang', code) setLangSheetOpen(false) } useEffect(() => { fetchMegaMenu() const checkAnnouncement = () => { const el = document.querySelector('.announcement-bar') setHasAnnouncement(!!el) } checkAnnouncement() const timer = setTimeout(checkAnnouncement, 300) const onScroll = () => { setScrolled(window.scrollY > 20) checkAnnouncement() } window.addEventListener('scroll', onScroll, { passive: true }) return () => { window.removeEventListener('scroll', onScroll) clearTimeout(timer) } }, [fetchMegaMenu]) useEffect(() => { fetch(`${API_BASE}/settings`) .then((response) => (response.ok ? response.json() : null)) .then((data: StoreSettings | null) => { if (data) setSettings(data) }) .catch((error) => console.error('Failed to fetch store settings', error)) }, []) // Keep --navbar-bottom in sync so the fixed mega-menu dropdown sits flush below the navbar useEffect(() => { let animationFrameId: number; const navElement = navRef.current; const updateNavBottom = () => { if (navElement) { const bottom = navElement.getBoundingClientRect().bottom document.documentElement.style.setProperty('--navbar-bottom', `${bottom}px`) } } updateNavBottom() window.addEventListener('scroll', updateNavBottom, { passive: true }) window.addEventListener('resize', updateNavBottom) const observer = new ResizeObserver(() => { cancelAnimationFrame(animationFrameId); animationFrameId = requestAnimationFrame(updateNavBottom); }); if (navElement) { observer.observe(navElement); navElement.addEventListener('transitionend', updateNavBottom); } observer.observe(document.body); return () => { window.removeEventListener('scroll', updateNavBottom) window.removeEventListener('resize', updateNavBottom) observer.disconnect() if (navElement) { navElement.removeEventListener('transitionend', updateNavBottom); } cancelAnimationFrame(animationFrameId); } }, [topOffsetClassName]) useEffect(() => { setMobileOpen(false) }, [navigate]) useEffect(() => { document.body.style.overflow = (searchOpen || mobileOpen || langSheetOpen) ? 'hidden' : '' return () => { document.body.style.overflow = '' } }, [searchOpen, mobileOpen, langSheetOpen]) const handleSearch = (e: React.FormEvent) => { e.preventDefault() if (searchQuery.trim()) { navigate(`/search?q=${encodeURIComponent(searchQuery.trim())}`) setSearchOpen(false) setMobileOpen(false) setSearchQuery('') } } const computedOffset = topOffsetClassName !== 'top-0' ? topOffsetClassName : (hasAnnouncement ? 'top-[32px] md:top-[40px]' : 'top-0') const finalTopClassName = scrolled ? 'top-0' : computedOffset const brandName = settings?.brandName || '' return ( <> {!isDesktop && ( setMobileOpen(false)} items={megaMenu} onOpenLang={() => setLangSheetOpen(true)} /> )} {searchOpen && (
{ if (e.target === e.currentTarget) setSearchOpen(false) }} >

{t('nav.search.title')}

setSearchQuery(e.target.value)} placeholder="ค้นหากลิ่น, หมวดหมู่..." className="bg-transparent border-none outline-none text-lg md:text-xl p-4 pl-0 w-full text-spa-forest placeholder-spa-stone/40" autoFocus />
)}
setLangSheetOpen(false)} />

Select Language

{LANGUAGES.map(lang => { const isActive = i18n.language.startsWith(lang.code) return ( ) })}
) }