x
This commit is contained in:
1 parent
9941798a85
commit
1201bfeed7
6 files changed
+320
-75
No files matched your search
@@ -43,6 +43,7 @@ export default function ThemeSettingsPage() {
|
|||||||
const [scrollRevealEnabled, setScrollRevealEnabled] = useState(true)
|
const [scrollRevealEnabled, setScrollRevealEnabled] = useState(true)
|
||||||
const [scrollRevealStyle, setScrollRevealStyle] = useState('fade-up')
|
const [scrollRevealStyle, setScrollRevealStyle] = useState('fade-up')
|
||||||
const [scrollRevealDuration, setScrollRevealDuration] = useState('700')
|
const [scrollRevealDuration, setScrollRevealDuration] = useState('700')
|
||||||
|
const [headerStyle, setHeaderStyle] = useState('single_line')
|
||||||
|
|
||||||
const fontPreset = resolveFontPreset(themeTokens)
|
const fontPreset = resolveFontPreset(themeTokens)
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ export default function ThemeSettingsPage() {
|
|||||||
setScrollRevealEnabled(data.scrollRevealEnabled ?? true)
|
setScrollRevealEnabled(data.scrollRevealEnabled ?? true)
|
||||||
setScrollRevealStyle(data.scrollRevealStyle || 'fade-up')
|
setScrollRevealStyle(data.scrollRevealStyle || 'fade-up')
|
||||||
setScrollRevealDuration((data.scrollRevealDuration ?? 700).toString())
|
setScrollRevealDuration((data.scrollRevealDuration ?? 700).toString())
|
||||||
|
setHeaderStyle(data.headerStyle || 'single_line')
|
||||||
})
|
})
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
.finally(() => setLoading(false))
|
.finally(() => setLoading(false))
|
||||||
@@ -76,6 +78,7 @@ export default function ThemeSettingsPage() {
|
|||||||
scrollRevealEnabled,
|
scrollRevealEnabled,
|
||||||
scrollRevealStyle,
|
scrollRevealStyle,
|
||||||
scrollRevealDuration: parseInt(scrollRevealDuration) || 700,
|
scrollRevealDuration: parseInt(scrollRevealDuration) || 700,
|
||||||
|
headerStyle,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
if (res.ok) alert('Saved successfully!')
|
if (res.ok) alert('Saved successfully!')
|
||||||
@@ -132,6 +135,66 @@ export default function ThemeSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Header Layout Style */}
|
||||||
|
<div className="admin-card p-8 space-y-6">
|
||||||
|
<div className="flex items-center gap-3 border-b border-white/10 pb-4">
|
||||||
|
<div className="p-2 bg-amber-500/10 rounded-lg"><span className="text-xl">🧭</span></div>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-lg font-bold uppercase tracking-widest text-white">PC Header Layout Style</h2>
|
||||||
|
<p className="text-xs text-slate-400 mt-0.5">เลือกรูปแบบการจัดวางเมนูส่วนหัวหลักสำหรับหน้าจอแนวกว้าง (PC / Desktop View)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setHeaderStyle('single_line')}
|
||||||
|
className={`flex flex-col gap-3 rounded-2xl border p-5 text-left transition ${
|
||||||
|
headerStyle === 'single_line'
|
||||||
|
? 'border-amber-500 bg-amber-500/10 text-white'
|
||||||
|
: 'border-white/10 bg-white/5 text-slate-400 hover:border-white/20'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm font-bold text-white">Single Line (มาตรฐานแถบเดียว)</span>
|
||||||
|
{headerStyle === 'single_line' && <span className="text-xs font-bold uppercase text-amber-400">Selected</span>}
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-400">โลโก้ซ้าย/กลาง + เมนูกลาง + ไอคอนขวา อยู่ในแถบเดียว ประหยัดพื้นที่แนวตั้ง</p>
|
||||||
|
<div className="h-10 w-full rounded-lg border border-white/10 bg-slate-900/80 p-2 flex items-center justify-between text-[9px] text-slate-400">
|
||||||
|
<div className="font-bold text-white">LOGO</div>
|
||||||
|
<div className="flex gap-2"><span>Shop</span><span>Category</span><span>About</span></div>
|
||||||
|
<div className="flex gap-1"><span>🔍</span><span>🛒</span></div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setHeaderStyle('two_tier')}
|
||||||
|
className={`flex flex-col gap-3 rounded-2xl border p-5 text-left transition ${
|
||||||
|
headerStyle === 'two_tier'
|
||||||
|
? 'border-amber-500 bg-amber-500/10 text-white'
|
||||||
|
: 'border-white/10 bg-white/5 text-slate-400 hover:border-white/20'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-sm font-bold text-white">Two-Tier Luxury (หรูหรา 2 ชั้น)</span>
|
||||||
|
{headerStyle === 'two_tier' && <span className="text-xs font-bold uppercase text-amber-400">Selected</span>}
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-slate-400">แบบแบรนด์เนมชั้นนำ (Chanel Style): โลโก้กลาง + ไอคอนขวา (ชั้น 1) / เมนูกลาง (ชั้น 2)</p>
|
||||||
|
<div className="h-12 w-full rounded-lg border border-white/10 bg-slate-900/80 p-2 flex flex-col justify-between text-[9px] text-slate-400">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="w-4" />
|
||||||
|
<div className="font-bold text-white text-[10px]">BRAND LOGO</div>
|
||||||
|
<div className="flex gap-1"><span>🔍</span><span>🛒</span></div>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center gap-3 border-t border-white/5 pt-1 text-[8px]">
|
||||||
|
<span>HAUTE COUTURE</span><span>FASHION</span><span>PERFUME</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Scroll Reveal */}
|
{/* Scroll Reveal */}
|
||||||
<div className="admin-card p-8 space-y-6">
|
<div className="admin-card p-8 space-y-6">
|
||||||
<div className="flex items-center gap-3 border-b border-white/10 pb-4">
|
<div className="flex items-center gap-3 border-b border-white/10 pb-4">
|
||||||
|
|||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "StoreSettings" ADD COLUMN "headerStyle" TEXT NOT NULL DEFAULT 'single_line';
|
||||||
@@ -278,6 +278,7 @@ model StoreSettings {
|
|||||||
scrollRevealEnabled Boolean @default(true)
|
scrollRevealEnabled Boolean @default(true)
|
||||||
scrollRevealStyle String @default("fade-up")
|
scrollRevealStyle String @default("fade-up")
|
||||||
scrollRevealDuration Int @default(700)
|
scrollRevealDuration Int @default(700)
|
||||||
|
headerStyle String @default("single_line")
|
||||||
translations StoreSettingsTranslation[]
|
translations StoreSettingsTranslation[]
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ export const settingsRoute = new Elysia({ prefix: "/settings" })
|
|||||||
scrollRevealEnabled: t.Optional(t.Boolean()),
|
scrollRevealEnabled: t.Optional(t.Boolean()),
|
||||||
scrollRevealStyle: t.Optional(t.String()),
|
scrollRevealStyle: t.Optional(t.String()),
|
||||||
scrollRevealDuration: t.Optional(t.Number()),
|
scrollRevealDuration: t.Optional(t.Number()),
|
||||||
|
headerStyle: t.Optional(t.String()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.patch("/translations", async ({ body, set }) => {
|
.patch("/translations", async ({ body, set }) => {
|
||||||
|
|||||||
@@ -215,12 +215,16 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
|
|||||||
return () => mediaQuery.removeEventListener('change', updateDesktopMenu)
|
return () => mediaQuery.removeEventListener('change', updateDesktopMenu)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const isTwoTier = settings?.headerStyle === 'two_tier'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<nav
|
<nav
|
||||||
ref={navRef}
|
ref={navRef}
|
||||||
data-scrolled={scrolled}
|
data-scrolled={scrolled}
|
||||||
className={`global-navbar fixed top-0 z-50 w-full border-b px-4 py-3.5 transition-all duration-300 md:px-6 md:py-4 ${
|
className={`global-navbar fixed top-0 z-50 w-full border-b transition-all duration-300 ${
|
||||||
|
isTwoTier ? 'px-4 py-2.5 md:px-8 md:py-3' : 'px-4 py-3.5 md:px-6 md:py-4'
|
||||||
|
} ${
|
||||||
hideDesktopHeader ? 'md:pointer-events-none md:-translate-y-full' : 'translate-y-0'
|
hideDesktopHeader ? 'md:pointer-events-none md:-translate-y-full' : 'translate-y-0'
|
||||||
} ${
|
} ${
|
||||||
scrolled
|
scrolled
|
||||||
@@ -230,72 +234,81 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
|
|||||||
: 'border-black/5 bg-surface-page/80 backdrop-blur-xl md:bg-surface-nav-rest md:backdrop-blur-none'
|
: 'border-black/5 bg-surface-page/80 backdrop-blur-xl md:bg-surface-nav-rest md:backdrop-blur-none'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex w-full items-center justify-between gap-4">
|
{isTwoTier ? (
|
||||||
<div className="flex shrink-0 items-center">
|
<div className="flex w-full flex-col">
|
||||||
<Link
|
{/* Top Tier: Logo Centered + Icons Right (Desktop) & Mobile Top Row */}
|
||||||
href="/"
|
<div className={`flex w-full items-center justify-between transition-all duration-300 md:grid md:grid-cols-3 ${
|
||||||
onClick={() => setMobileOpen(false)}
|
scrolled ? 'max-h-0 opacity-0 overflow-hidden py-0' : 'max-h-24 py-1.5 opacity-100'
|
||||||
className="ui-brand-wordmark"
|
}`}>
|
||||||
>
|
<div className="flex items-center">
|
||||||
{settings?.logoUrl ? (
|
{isClientReady && !canRenderDesktopMenu && (
|
||||||
<Image src={settings.logoUrl} alt={brandName} width={160} height={40} unoptimized className="h-8 w-auto object-contain md:h-10" />
|
<button
|
||||||
) : (
|
onClick={() => setMobileOpen((prev) => !prev)}
|
||||||
brandName
|
className={iconButtonClass}
|
||||||
)}
|
aria-label={mobileOpen ? 'Close menu' : 'Open menu'}
|
||||||
</Link>
|
>
|
||||||
</div>
|
{mobileOpen ? <X className="h-5 w-5" /> : <MenuIcon className="h-5 w-5" />}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-1 justify-center">
|
<div className="flex items-center justify-center text-center">
|
||||||
{canRenderDesktopMenu && (
|
<Link
|
||||||
<DesktopMegaMenu items={menuItems} lang={lang} currentPath={pathname} />
|
href="/"
|
||||||
)}
|
onClick={() => setMobileOpen(false)}
|
||||||
</div>
|
className="ui-brand-wordmark text-center"
|
||||||
|
>
|
||||||
|
{settings?.logoUrl ? (
|
||||||
|
<Image src={settings.logoUrl} alt={brandName} width={180} height={45} unoptimized className="h-9 w-auto object-contain md:h-11" />
|
||||||
|
) : (
|
||||||
|
<span className="text-2xl font-bold uppercase tracking-[0.25em] text-text-main md:text-3xl">{brandName}</span>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex shrink-0 items-center gap-1.5 md:gap-2">
|
<div className="flex items-center justify-end gap-1.5 md:gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSearchOpen(true)
|
setSearchOpen(true)
|
||||||
setMobileOpen(false)
|
setMobileOpen(false)
|
||||||
}}
|
}}
|
||||||
className={iconButtonClass}
|
className={iconButtonClass}
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
>
|
>
|
||||||
<Search className="h-4 w-4 transition-colors" />
|
<Search className="h-4 w-4 transition-colors" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href="/wishlist"
|
href="/wishlist"
|
||||||
onClick={() => setMobileOpen(false)}
|
onClick={() => setMobileOpen(false)}
|
||||||
className={`relative ${iconButtonClass}`}
|
className={`relative ${iconButtonClass}`}
|
||||||
aria-label="Wishlist"
|
aria-label="Wishlist"
|
||||||
>
|
>
|
||||||
<Heart className="h-4 w-4 transition-colors" />
|
<Heart className="h-4 w-4 transition-colors" />
|
||||||
{wishlistCount > 0 && (
|
{wishlistCount > 0 && (
|
||||||
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
||||||
{wishlistCount}
|
{wishlistCount}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleCart(true)
|
toggleCart(true)
|
||||||
setMobileOpen(false)
|
setMobileOpen(false)
|
||||||
}}
|
}}
|
||||||
className={`relative ${iconButtonClass}`}
|
className={`relative ${iconButtonClass}`}
|
||||||
aria-label="Cart"
|
aria-label="Cart"
|
||||||
>
|
>
|
||||||
<ShoppingBag className="h-4 w-4 transition-colors" />
|
<ShoppingBag className="h-4 w-4 transition-colors" />
|
||||||
{cartCount > 0 && (
|
{cartCount > 0 && (
|
||||||
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
||||||
{cartCount}
|
{cartCount}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{canRenderDesktopMenu && (
|
<div className="hidden md:flex group relative items-center">
|
||||||
<>
|
|
||||||
<div className="group relative flex items-center">
|
|
||||||
<button className="ui-nav-chip">
|
<button className="ui-nav-chip">
|
||||||
<Globe className="h-3.5 w-3.5" strokeWidth={1.5} />
|
<Globe className="h-3.5 w-3.5" strokeWidth={1.5} />
|
||||||
{activeLanguage.short}
|
{activeLanguage.short}
|
||||||
@@ -321,7 +334,7 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="relative flex items-center">
|
<div className="hidden md:flex relative items-center">
|
||||||
{customer ? (
|
{customer ? (
|
||||||
<Link href="/profile" className="ml-1">
|
<Link href="/profile" className="ml-1">
|
||||||
{customer.pictureUrl ? (
|
{customer.pictureUrl ? (
|
||||||
@@ -348,20 +361,184 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
|
||||||
{isClientReady && !canRenderDesktopMenu && (
|
{/* Bottom Tier: Centered Navigation (Desktop only) */}
|
||||||
<button
|
<div className={`hidden md:flex w-full items-center justify-between transition-all duration-300 ${
|
||||||
onClick={() => setMobileOpen((prev) => !prev)}
|
scrolled ? 'py-0.5' : 'border-t border-black/5 pt-2 mt-1'
|
||||||
className={iconButtonClass}
|
}`}>
|
||||||
aria-label={mobileOpen ? 'Close menu' : 'Open menu'}
|
<div className={`transition-all duration-300 ${scrolled ? 'w-auto opacity-100' : 'w-0 opacity-0 overflow-hidden'}`}>
|
||||||
>
|
<Link href="/" className="ui-brand-wordmark text-sm font-bold tracking-widest">
|
||||||
{mobileOpen ? <X className="h-5 w-5" /> : <MenuIcon className="h-5 w-5" />}
|
{settings?.logoUrl ? (
|
||||||
</button>
|
<Image src={settings.logoUrl} alt={brandName} width={120} height={28} unoptimized className="h-6 w-auto object-contain" />
|
||||||
)}
|
) : (
|
||||||
|
brandName
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-1 justify-center">
|
||||||
|
<DesktopMegaMenu items={menuItems} lang={lang} currentPath={pathname} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={`flex items-center gap-1.5 transition-all duration-300 ${scrolled ? 'w-auto opacity-100 md:gap-2' : 'w-0 opacity-0 overflow-hidden'}`}>
|
||||||
|
<button onClick={() => setSearchOpen(true)} className={iconButtonClass} aria-label="Search">
|
||||||
|
<Search className="h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
<Link href="/wishlist" className={`relative ${iconButtonClass}`} aria-label="Wishlist">
|
||||||
|
<Heart className="h-4 w-4" />
|
||||||
|
{wishlistCount > 0 && (
|
||||||
|
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
||||||
|
{wishlistCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
<button onClick={() => toggleCart(true)} className={`relative ${iconButtonClass}`} aria-label="Cart">
|
||||||
|
<ShoppingBag className="h-4 w-4" />
|
||||||
|
{cartCount > 0 && (
|
||||||
|
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
||||||
|
{cartCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
<div className="flex w-full items-center justify-between gap-4">
|
||||||
|
<div className="flex shrink-0 items-center">
|
||||||
|
<Link
|
||||||
|
href="/"
|
||||||
|
onClick={() => setMobileOpen(false)}
|
||||||
|
className="ui-brand-wordmark"
|
||||||
|
>
|
||||||
|
{settings?.logoUrl ? (
|
||||||
|
<Image src={settings.logoUrl} alt={brandName} width={160} height={40} unoptimized className="h-8 w-auto object-contain md:h-10" />
|
||||||
|
) : (
|
||||||
|
brandName
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-1 justify-center">
|
||||||
|
{canRenderDesktopMenu && (
|
||||||
|
<DesktopMegaMenu items={menuItems} lang={lang} currentPath={pathname} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex shrink-0 items-center gap-1.5 md:gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setSearchOpen(true)
|
||||||
|
setMobileOpen(false)
|
||||||
|
}}
|
||||||
|
className={iconButtonClass}
|
||||||
|
aria-label="Search"
|
||||||
|
>
|
||||||
|
<Search className="h-4 w-4 transition-colors" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
href="/wishlist"
|
||||||
|
onClick={() => setMobileOpen(false)}
|
||||||
|
className={`relative ${iconButtonClass}`}
|
||||||
|
aria-label="Wishlist"
|
||||||
|
>
|
||||||
|
<Heart className="h-4 w-4 transition-colors" />
|
||||||
|
{wishlistCount > 0 && (
|
||||||
|
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
||||||
|
{wishlistCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
toggleCart(true)
|
||||||
|
setMobileOpen(false)
|
||||||
|
}}
|
||||||
|
className={`relative ${iconButtonClass}`}
|
||||||
|
aria-label="Cart"
|
||||||
|
>
|
||||||
|
<ShoppingBag className="h-4 w-4 transition-colors" />
|
||||||
|
{cartCount > 0 && (
|
||||||
|
<span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-brand-primary text-[9px] font-bold text-surface-card">
|
||||||
|
{cartCount}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{canRenderDesktopMenu && (
|
||||||
|
<>
|
||||||
|
<div className="group relative flex items-center">
|
||||||
|
<button className="ui-nav-chip">
|
||||||
|
<Globe className="h-3.5 w-3.5" strokeWidth={1.5} />
|
||||||
|
{activeLanguage.short}
|
||||||
|
</button>
|
||||||
|
<div className="pointer-events-none absolute right-0 top-full w-36 pt-2 opacity-0 transition-all duration-300 group-hover:pointer-events-auto group-hover:opacity-100">
|
||||||
|
<div className="ui-nav-panel space-y-0.5 p-1.5">
|
||||||
|
{LANGUAGES.map((language) => {
|
||||||
|
const isActive = lang.startsWith(language.code) || language.code.startsWith(lang)
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={language.code}
|
||||||
|
onClick={() => changeLanguage(language.code)}
|
||||||
|
className={`flex w-full items-center justify-between rounded-lg px-3 py-2.5 text-xs font-medium transition-colors ${
|
||||||
|
isActive ? 'bg-surface-page font-bold text-text-main' : 'text-text-main/65 hover:bg-surface-page hover:text-brand-primary'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span>{language.label}</span>
|
||||||
|
{isActive && <Check className="h-3.5 w-3.5" />}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative flex items-center">
|
||||||
|
{customer ? (
|
||||||
|
<Link href="/profile" className="ml-1">
|
||||||
|
{customer.pictureUrl ? (
|
||||||
|
<Image
|
||||||
|
src={customer.pictureUrl}
|
||||||
|
alt={customer.displayName || 'Profile'}
|
||||||
|
width={36}
|
||||||
|
height={36}
|
||||||
|
unoptimized
|
||||||
|
className="h-9 w-9 rounded-full border-2 border-brand-primary/30 object-cover"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="flex h-9 w-9 items-center justify-center rounded-full border-2 border-brand-primary/30 text-text-main/65">
|
||||||
|
<User className="h-4 w-4" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={loginWithLine}
|
||||||
|
className="ui-button-line ml-2"
|
||||||
|
>
|
||||||
|
{t('nav.login', 'เข้าสู่ระบบด้วย LINE')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isClientReady && !canRenderDesktopMenu && (
|
||||||
|
<button
|
||||||
|
onClick={() => setMobileOpen((prev) => !prev)}
|
||||||
|
className={iconButtonClass}
|
||||||
|
aria-label={mobileOpen ? 'Close menu' : 'Open menu'}
|
||||||
|
>
|
||||||
|
{mobileOpen ? <X className="h-5 w-5" /> : <MenuIcon className="h-5 w-5" />}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{isClientReady && mobileOpen && (
|
{isClientReady && mobileOpen && (
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ export interface StoreSettings {
|
|||||||
scrollRevealEnabled?: boolean | null
|
scrollRevealEnabled?: boolean | null
|
||||||
scrollRevealStyle?: string | null
|
scrollRevealStyle?: string | null
|
||||||
scrollRevealDuration?: number | null
|
scrollRevealDuration?: number | null
|
||||||
|
headerStyle?: 'single_line' | 'two_tier' | string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FooterBrand {
|
export interface FooterBrand {
|
||||||
|
|||||||
Reference in new issue
Block a user