"use client" import Link from 'next/link' import { useRouter, usePathname } from 'next/navigation' import { useState, useEffect, useMemo, useRef } from 'react' import type { Category } from '@homaroma/types' import { Check, Globe, Heart, Menu as MenuIcon, ShoppingBag, User, X } from 'lucide-react' import { useCartStore } from '../store/cartStore' import { useWishlistStore } from '../store/wishlistStore' import { useAuthStore } from '../store/authStore' import { useTranslation } from 'react-i18next' import { resolveLocalized } from '../lib/localization' import { API_BASE } from '../lib/api' import { DesktopMegaMenu } from './navigation/DesktopMegaMenu' import { MobileDrawer } from './navigation/MobileDrawer' import type { MegaMenuPayload } from './navigation/menuTypes' interface NavbarProps { categories?: Category[] } 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' }, ] function createFallbackMenu(categories: Category[]): MegaMenuPayload[] { const categoryLinks = categories.map((category) => ({ label: { th: resolveLocalized(category.name, 'th') || resolveLocalized(category.name), en: resolveLocalized(category.name, 'en') || resolveLocalized(category.name), }, url: `/category/${category.id}`, })) return [ { id: 'home', label: { th: 'หน้าหลัก', en: 'Home' }, url: '/' }, { id: 'shop', label: { th: 'ร้านค้า', en: 'Shop' }, url: '/shop', columns: categoryLinks.length ? [ { title: { th: 'คอลเลกชัน', en: 'Collections' }, links: categoryLinks, }, ] : undefined, }, { id: 'promotions', label: { th: 'โปรโมชัน', en: 'Promotions' }, url: '/coupons' }, { id: 'scent-guide', label: { th: 'คลาสแนะนำกลิ่น', en: 'Scent Guide' }, url: '/scent-guide' }, { id: 'about', label: { th: 'เกี่ยวกับเรา', en: 'About' }, url: '/about' }, ] } export default function Navbar({ categories = [] }: NavbarProps) { 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 [megaMenu, setMegaMenu] = useState([]) const [menuLoaded, setMenuLoaded] = useState(false) const navRef = useRef(null) const router = useRouter() const pathname = usePathname() const { toggleCart, getCartCount } = useCartStore() const wishlistCount = useWishlistStore((state) => state.items.length) const customer = useAuthStore((state) => state.customer) const logout = useAuthStore((state) => state.logout) const cartCount = getCartCount() const { t, i18n } = useTranslation() const lang = i18n.language ? i18n.language.substring(0, 2) : 'th' const fallbackMenu = useMemo(() => createFallbackMenu(categories), [categories]) const menuItems = menuLoaded && megaMenu.length > 0 ? megaMenu : fallbackMenu const changeLanguage = (code: string) => { i18n.changeLanguage(code) localStorage.setItem('homaroma_lang', code) setLangSheetOpen(false) } const loginWithLine = () => { const maybeWindow = window as Window & { liff?: { login: () => void } } if (maybeWindow.liff?.login) { maybeWindow.liff.login() return } router.push('/profile') } useEffect(() => { let isMounted = true async function fetchMegaMenu() { try { const response = await fetch(`${API_BASE}/menus/main`) if (!response.ok) throw new Error(`menus/main returned ${response.status}`) const data = await response.json() as { payload?: MegaMenuPayload[] } if (isMounted) { setMegaMenu(Array.isArray(data.payload) ? data.payload : []) setMenuLoaded(true) } } catch (error) { console.error('[website-menu] Failed to fetch main menu', error) if (isMounted) setMenuLoaded(true) } } fetchMegaMenu() return () => { isMounted = false } }, []) useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 20) window.addEventListener('scroll', onScroll, { passive: true }) onScroll() return () => window.removeEventListener('scroll', onScroll) }, []) useEffect(() => { const updateNavBottom = () => { if (navRef.current) { const bottom = navRef.current.getBoundingClientRect().bottom document.documentElement.style.setProperty('--navbar-bottom', `${bottom}px`) } } updateNavBottom() window.addEventListener('scroll', updateNavBottom, { passive: true }) window.addEventListener('resize', updateNavBottom) return () => { window.removeEventListener('scroll', updateNavBottom) window.removeEventListener('resize', updateNavBottom) } }, []) 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()) { router.push(`/search?q=${encodeURIComponent(searchQuery.trim())}`) setSearchOpen(false) setMobileOpen(false) setSearchQuery('') } } const iconButtonClass = 'rounded-full p-1.5 text-pop-black/65 transition-all hover:bg-pop-bg hover:text-pop-pink md:p-2' const activeLanguage = LANGUAGES.find((language) => i18n.language.startsWith(language.code)) ?? LANGUAGES[0] return ( <> setMobileOpen(false)} onOpenLang={() => setLangSheetOpen(true)} onLogin={loginWithLine} onLogout={logout} /> {searchOpen && (
{ if (e.target === e.currentTarget) setSearchOpen(false) }} >

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

setSearchQuery(e.target.value)} placeholder={t('nav.search.placeholder')} className="w-full border-none bg-transparent p-4 pl-0 text-lg text-pop-black outline-none placeholder:text-pop-black/35 md:text-xl" autoFocus />

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

{[ ...categories.map((category) => ({ label: resolveLocalized(category.name), to: `/category/${category.id}` })), { label: t('nav.scentGuide'), to: '/scent-guide' }, { label: t('nav.search.allProducts'), to: '/shop' }, ].map((item) => ( setSearchOpen(false)} className="rounded-full border border-pop-black/10 bg-white px-4 py-1.5 text-xs font-semibold text-pop-black/65 transition-all hover:border-pop-pink hover:text-pop-pink" > {item.label} ))}
)}
setLangSheetOpen(false)} />

Select Language

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