"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 { useLang } from '../hooks/useLang' import { DesktopMegaMenu } from './navigation/DesktopMegaMenu' import { MobileDrawer } from './navigation/MobileDrawer' import type { MegaMenuPayload } from './navigation/menuTypes' import type { StoreSettings } from '@homaroma/types' interface NavbarProps { categories?: Category[] initialMenu?: MegaMenuPayload[] settings?: StoreSettings | 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' }, ] function createFallbackMenu(categories: Category[]): MegaMenuPayload[] { const categoryLinks = categories.map((category) => { // category.name is a multilingual Record when fetched with lang=all const label: Record = typeof category.name === 'string' ? { th: category.name, en: category.name } : (category.name as Record) return { label, 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 = [], initialMenu = [], settings = null }: 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) // Seed from server-fetched initialMenu — no client flash const [megaMenu] = useState(initialMenu) 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 = useLang() const fallbackMenu = useMemo(() => createFallbackMenu(categories), [categories]) // Use server menu if available, fall back to categories-based menu const menuItems = 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(() => { const onScroll = () => setScrolled(window.scrollY > 20) window.addEventListener('scroll', onScroll, { passive: true }) onScroll() return () => window.removeEventListener('scroll', onScroll) }, []) 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); } }, [scrolled]) 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 = 'group rounded-full p-2 text-pop-black transition-all hover:bg-pop-pink/10 hover:text-pop-pink md:p-2.5' const activeLanguage = LANGUAGES.find((language) => lang.startsWith(language.code) || language.code.startsWith(lang)) ?? LANGUAGES[0] const brandName = settings?.brandName || '' 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, lang), 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 = lang.startsWith(language.code) || language.code.startsWith(lang) return ( ) })}
) }