x
This commit is contained in:
1 parent
3cacaacbeb
commit
8b73b8dd2f
52 files changed
+265
-265
No files matched your search
@@ -32,7 +32,7 @@ export default function CartDrawer() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Drawer */}
|
{/* Drawer */}
|
||||||
<div className="relative w-full max-w-md h-full bg-white shadow-2xl flex flex-col animate-slide-left">
|
<div className="relative w-full max-w-md h-full bg-spa-light shadow-2xl flex flex-col animate-slide-left">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="p-6 border-b border-spa-stone/10 flex items-center justify-between bg-spa-light">
|
<div className="p-6 border-b border-spa-stone/10 flex items-center justify-between bg-spa-light">
|
||||||
<h2 className="text-xl font-bold flex items-center gap-2 text-pop-black">
|
<h2 className="text-xl font-bold flex items-center gap-2 text-pop-black">
|
||||||
@@ -65,7 +65,7 @@ export default function CartDrawer() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
items.map((item) => (
|
items.map((item) => (
|
||||||
<div key={item.cartItemId} className="flex gap-4 p-4 rounded-2xl bg-white border border-spa-stone/10 shadow-sm group relative">
|
<div key={item.cartItemId} className="flex gap-4 p-4 rounded-2xl bg-spa-light border border-spa-stone/10 shadow-sm group relative">
|
||||||
<div className="w-18 h-22 rounded-lg bg-spa-sage/30 overflow-hidden flex-shrink-0" style={{ width: '72px', height: '88px' }}>
|
<div className="w-18 h-22 rounded-lg bg-spa-sage/30 overflow-hidden flex-shrink-0" style={{ width: '72px', height: '88px' }}>
|
||||||
<img
|
<img
|
||||||
src={item.productImage || '/images/fallback-product.svg'}
|
src={item.productImage || '/images/fallback-product.svg'}
|
||||||
@@ -225,7 +225,7 @@ function CartDrawerFooter({ subtotal, onClose, t }: { subtotal: number; onClose:
|
|||||||
value={couponInput}
|
value={couponInput}
|
||||||
onChange={e => setCouponInput(e.target.value.toUpperCase())}
|
onChange={e => setCouponInput(e.target.value.toUpperCase())}
|
||||||
placeholder={t('cart.couponPlaceholder', 'Discount Code')}
|
placeholder={t('cart.couponPlaceholder', 'Discount Code')}
|
||||||
className="flex-1 bg-white border border-spa-stone/20 rounded-lg px-3 py-2 text-sm text-pop-black focus:border-spa-forest outline-none uppercase tracking-widest placeholder:text-spa-stone/50"
|
className="flex-1 bg-spa-light border border-spa-stone/20 rounded-lg px-3 py-2 text-sm text-pop-black focus:border-spa-forest outline-none uppercase tracking-widest placeholder:text-spa-stone/50"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleApplyCoupon}
|
onClick={handleApplyCoupon}
|
||||||
@@ -292,7 +292,7 @@ function CartDrawerFooter({ subtotal, onClose, t }: { subtotal: number; onClose:
|
|||||||
<Link
|
<Link
|
||||||
to="/checkout"
|
to="/checkout"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="flex items-center justify-center gap-2 w-full py-4 bg-spa-forest text-white font-bold rounded-xl uppercase tracking-widest text-sm hover:bg-spa-stone hover:shadow-lg transition-all duration-300 group"
|
className="flex items-center justify-center gap-2 w-full py-4 bg-spa-forest text-spa-light font-bold rounded-xl uppercase tracking-widest text-sm hover:bg-spa-stone hover:shadow-lg transition-all duration-300 group"
|
||||||
>
|
>
|
||||||
<ShoppingBag className="w-4 h-4" />
|
<ShoppingBag className="w-4 h-4" />
|
||||||
ดำเนินการชำระเงิน
|
ดำเนินการชำระเงิน
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default function DynamicBanner({ payload }: DynamicBannerProps) {
|
|||||||
const buttonText = getLocalizedField(payload.buttonText, lang);
|
const buttonText = getLocalizedField(payload.buttonText, lang);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="ui-section-band bg-white relative overflow-hidden">
|
<section className="ui-section-band bg-spa-light relative overflow-hidden">
|
||||||
{linkUrl ? (
|
{linkUrl ? (
|
||||||
<Link to={linkUrl} className="ui-banner-shell group block cursor-pointer">
|
<Link to={linkUrl} className="ui-banner-shell group block cursor-pointer">
|
||||||
<div className="absolute inset-0 z-0">
|
<div className="absolute inset-0 z-0">
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export default function DynamicCategories({ payload, categories, loading }: Dyna
|
|||||||
}, [categories, categoryIds]);
|
}, [categories, categoryIds]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="categories" className="ui-section-band relative overflow-hidden bg-white">
|
<section id="categories" className="ui-section-band relative overflow-hidden bg-spa-light">
|
||||||
<div className="ui-section-inner space-y-16 relative z-10">
|
<div className="ui-section-inner space-y-16 relative z-10">
|
||||||
<div className="space-y-4 text-center">
|
<div className="space-y-4 text-center">
|
||||||
<h2 className="text-4xl font-bold tracking-tight text-pop-black md:text-5xl">
|
<h2 className="text-4xl font-bold tracking-tight text-pop-black md:text-5xl">
|
||||||
@@ -40,7 +40,7 @@ export default function DynamicCategories({ payload, categories, loading }: Dyna
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="grid grid-cols-2 gap-6 md:grid-cols-5">
|
<div className="grid grid-cols-2 gap-6 md:grid-cols-5">
|
||||||
{Array.from({ length: 5 }).map((_, i) => (
|
{Array.from({ length: 5 }).map((_, i) => (
|
||||||
<div key={i} className="ui-shell-card h-80 animate-pulse bg-gray-50" />
|
<div key={i} className="ui-shell-card h-80 animate-pulse bg-spa-sage" />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -58,12 +58,12 @@ export default function DynamicCategories({ payload, categories, loading }: Dyna
|
|||||||
alt={getLocalizedField(cat.name, lang)}
|
alt={getLocalizedField(cat.name, lang)}
|
||||||
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }}
|
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }}
|
||||||
/>
|
/>
|
||||||
<div className="absolute bottom-6 left-6 right-6 z-20 text-white">
|
<div className="absolute bottom-6 left-6 right-6 z-20 text-spa-light">
|
||||||
<span className="mb-1 block text-xl font-semibold">
|
<span className="mb-1 block text-xl font-semibold">
|
||||||
{getLocalizedField(cat.name, localStorage.getItem('app_lang') || 'en')}
|
{getLocalizedField(cat.name, localStorage.getItem('app_lang') || 'en')}
|
||||||
</span>
|
</span>
|
||||||
{cat._count && (
|
{cat._count && (
|
||||||
<span className="text-sm font-medium text-white/80">
|
<span className="text-sm font-medium text-spa-light">
|
||||||
{cat._count.products} Products
|
{cat._count.products} Products
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export default function DynamicEditorialCarousel({ payload }: DynamicEditorialCa
|
|||||||
<div className="pt-2">
|
<div className="pt-2">
|
||||||
<Link
|
<Link
|
||||||
to={slide.buttonLink}
|
to={slide.buttonLink}
|
||||||
className="inline-flex items-center gap-2 text-xs font-bold uppercase tracking-widest text-spa-gold hover:text-white transition-colors group/link"
|
className="inline-flex items-center gap-2 text-xs font-bold uppercase tracking-widest text-spa-gold hover:text-spa-light transition-colors group/link"
|
||||||
>
|
>
|
||||||
{slide.buttonText}
|
{slide.buttonText}
|
||||||
<span className="group-hover/link:translate-x-1 transition-transform">→</span>
|
<span className="group-hover/link:translate-x-1 transition-transform">→</span>
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export default function DynamicEditorialTile({ payload }: DynamicEditorialTilePr
|
|||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<Link
|
<Link
|
||||||
to={buttonLink}
|
to={buttonLink}
|
||||||
className="ui-chip border-current px-8 py-3.5 hover:bg-spa-gold hover:border-spa-gold hover:text-white"
|
className="ui-chip border-current px-8 py-3.5 hover:bg-spa-gold hover:border-spa-gold hover:text-spa-light"
|
||||||
>
|
>
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default function DynamicHeroArticle({ payload }: DynamicHeroArticleProps)
|
|||||||
<div className="ui-hero-overlay" />
|
<div className="ui-hero-overlay" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="ui-hero-content text-white">
|
<div className="ui-hero-content text-spa-light">
|
||||||
{category && (
|
{category && (
|
||||||
<span className="mb-4 block text-sm font-bold uppercase tracking-widest text-spa-gold">
|
<span className="mb-4 block text-sm font-bold uppercase tracking-widest text-spa-gold">
|
||||||
{category}
|
{category}
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export default function DynamicNewsletter({ payload }: DynamicNewsletterProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{status === 'success' ? (
|
{status === 'success' ? (
|
||||||
<div className="p-6 bg-white/5 border border-spa-gold/30 rounded-2xl animate-pop-in space-y-2">
|
<div className="p-6 bg-spa-light border border-spa-gold/30 rounded-2xl animate-pop-in space-y-2">
|
||||||
<h4 className="text-spa-gold font-semibold text-lg">{t('newsletter.thankYou')}</h4>
|
<h4 className="text-spa-gold font-semibold text-lg">{t('newsletter.thankYou')}</h4>
|
||||||
<p className="text-xs text-spa-light/75">{t('newsletter.successMessage')}</p>
|
<p className="text-xs text-spa-light/75">{t('newsletter.successMessage')}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ export default function DynamicProducts({ payload, categories = [] }: DynamicPro
|
|||||||
return (
|
return (
|
||||||
<section className="ui-section-band relative bg-pop-bg">
|
<section className="ui-section-band relative bg-pop-bg">
|
||||||
<div className="ui-section-inner relative z-10 space-y-12">
|
<div className="ui-section-inner relative z-10 space-y-12">
|
||||||
<div className="flex items-end justify-between gap-6 border-b border-gray-200 pb-6 md:flex-row">
|
<div className="flex items-end justify-between gap-6 border-b border-spa-stone/20 pb-6 md:flex-row">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-4xl font-bold text-pop-black md:text-5xl">
|
<h2 className="text-4xl font-bold text-pop-black md:text-5xl">
|
||||||
{title || "Featured Picks"}
|
{title || "Featured Picks"}
|
||||||
</h2>
|
</h2>
|
||||||
{subtitle && (
|
{subtitle && (
|
||||||
<p className="text-gray-500 mt-2">{subtitle}</p>
|
<p className="text-spa-stone mt-2">{subtitle}</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
@@ -45,7 +45,7 @@ export default function DynamicProducts({ payload, categories = [] }: DynamicPro
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{products.length === 0 ? (
|
{products.length === 0 ? (
|
||||||
<div className="py-12 text-center text-gray-500">
|
<div className="py-12 text-center text-spa-stone">
|
||||||
No products found in this section
|
No products found in this section
|
||||||
</div>
|
</div>
|
||||||
) : layout === 'carousel' ? (
|
) : layout === 'carousel' ? (
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function DynamicQuoteCallout({ payload }: DynamicQuoteCalloutProp
|
|||||||
const role = getLocalizedField(payload.role, lang);
|
const role = getLocalizedField(payload.role, lang);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="ui-section-band bg-white">
|
<section className="ui-section-band bg-spa-light">
|
||||||
<div className="ui-section-inner text-center">
|
<div className="ui-section-inner text-center">
|
||||||
<svg className="w-12 h-12 mx-auto text-spa-gold/40 mb-8" fill="currentColor" viewBox="0 0 24 24">
|
<svg className="w-12 h-12 mx-auto text-spa-gold/40 mb-8" fill="currentColor" viewBox="0 0 24 24">
|
||||||
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z" />
|
<path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z" />
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default function DynamicText({ payload }: DynamicTextProps) {
|
|||||||
}[textAlign] || 'text-left';
|
}[textAlign] || 'text-left';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="ui-section-band bg-white relative">
|
<section className="ui-section-band bg-spa-light relative">
|
||||||
<div className={`ui-section-inner space-y-6 ${alignClass}`}>
|
<div className={`ui-section-inner space-y-6 ${alignClass}`}>
|
||||||
{title && (
|
{title && (
|
||||||
<h2 className="font-serif text-3xl font-semibold text-spa-forest md:text-4xl">
|
<h2 className="font-serif text-3xl font-semibold text-spa-forest md:text-4xl">
|
||||||
@@ -28,7 +28,7 @@ export default function DynamicText({ payload }: DynamicTextProps) {
|
|||||||
</h2>
|
</h2>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className="whitespace-pre-wrap text-base font-light leading-relaxed text-gray-600 md:text-lg"
|
className="whitespace-pre-wrap text-base font-light leading-relaxed text-spa-stone md:text-lg"
|
||||||
dangerouslySetInnerHTML={{ __html: content }}
|
dangerouslySetInnerHTML={{ __html: content }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,13 +17,13 @@ export default function EmptyState({
|
|||||||
actionClick
|
actionClick
|
||||||
}: EmptyStateProps) {
|
}: EmptyStateProps) {
|
||||||
const ButtonContent = () => (
|
const ButtonContent = () => (
|
||||||
<span className="inline-block mt-6 px-8 py-3 bg-spa-forest text-white rounded-xl text-sm font-bold uppercase tracking-widest hover:bg-spa-stone hover:shadow-lg transition-all cursor-pointer">
|
<span className="inline-block mt-6 px-8 py-3 bg-spa-forest text-spa-light rounded-xl text-sm font-bold uppercase tracking-widest hover:bg-spa-stone hover:shadow-lg transition-all cursor-pointer">
|
||||||
{actionText}
|
{actionText}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center space-y-4 text-center p-12 bg-white border border-spa-stone/10 rounded-2xl shadow-sm max-w-lg mx-auto my-8">
|
<div className="flex flex-col items-center justify-center space-y-4 text-center p-12 bg-spa-light border border-spa-stone/10 rounded-2xl shadow-sm max-w-lg mx-auto my-8">
|
||||||
<div className="w-16 h-16 bg-spa-sage/30 rounded-full flex items-center justify-center mb-2">
|
<div className="w-16 h-16 bg-spa-sage/30 rounded-full flex items-center justify-center mb-2">
|
||||||
<PackageOpen className="w-8 h-8 text-spa-forest" />
|
<PackageOpen className="w-8 h-8 text-spa-forest" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export default function ErrorState({
|
|||||||
showHomeButton = true
|
showHomeButton = true
|
||||||
}: ErrorStateProps) {
|
}: ErrorStateProps) {
|
||||||
const content = (
|
const content = (
|
||||||
<div className="flex flex-col items-center justify-center space-y-4 text-center p-8 bg-white border border-rose-500/20 rounded-2xl shadow-sm max-w-md mx-auto">
|
<div className="flex flex-col items-center justify-center space-y-4 text-center p-8 bg-spa-light border border-rose-500/20 rounded-2xl shadow-sm max-w-md mx-auto">
|
||||||
<div className="w-12 h-12 bg-rose-500/10 rounded-full flex items-center justify-center">
|
<div className="w-12 h-12 bg-rose-500/10 rounded-full flex items-center justify-center">
|
||||||
<AlertCircle className="w-6 h-6 text-rose-500" />
|
<AlertCircle className="w-6 h-6 text-rose-500" />
|
||||||
</div>
|
</div>
|
||||||
@@ -21,7 +21,7 @@ export default function ErrorState({
|
|||||||
{showHomeButton && (
|
{showHomeButton && (
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="mt-4 px-6 py-2 bg-spa-forest text-white rounded-lg text-sm font-bold uppercase tracking-widest hover:bg-spa-stone transition-colors"
|
className="mt-4 px-6 py-2 bg-spa-forest text-spa-light rounded-lg text-sm font-bold uppercase tracking-widest hover:bg-spa-stone transition-colors"
|
||||||
>
|
>
|
||||||
Return Home
|
Return Home
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -71,31 +71,31 @@ export default function Footer({ categories = [] }: FooterProps) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="bg-night-900 px-6 py-20 border-t border-white/5">
|
<footer className="bg-night-900 px-6 py-20 border-t border-spa-light">
|
||||||
<div className="ui-section-inner grid grid-cols-1 gap-12 md:grid-cols-4">
|
<div className="ui-section-inner grid grid-cols-1 gap-12 md:grid-cols-4">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="text-2xl font-bold tracking-widest">
|
<div className="text-2xl font-bold tracking-widest">
|
||||||
{settings?.logoUrl ? <img src={settings.logoUrl} alt={brandName} className="h-8 md:h-10 object-contain" /> : brandName}
|
{settings?.logoUrl ? <img src={settings.logoUrl} alt={brandName} className="h-8 md:h-10 object-contain" /> : brandName}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm leading-relaxed text-slate-500">
|
<p className="text-sm leading-relaxed text-spa-stone">
|
||||||
{settings?.footerDescription || t('footer.catchphrase')}
|
{settings?.footerDescription || t('footer.catchphrase')}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
{settings && (settings.facebookUrl || settings.instagramUrl || settings.tiktokUrl) ? (
|
{settings && (settings.facebookUrl || settings.instagramUrl || settings.tiktokUrl) ? (
|
||||||
<>
|
<>
|
||||||
{settings.facebookUrl && (
|
{settings.facebookUrl && (
|
||||||
<a href={settings.facebookUrl} target="_blank" rel="noreferrer" className="ui-chip h-9 w-9 border-white/10 bg-white/5 text-[10px] text-white hover:bg-spa-gold hover:text-night-900">FB</a>
|
<a href={settings.facebookUrl} target="_blank" rel="noreferrer" className="ui-chip h-9 w-9 border-spa-light bg-spa-light text-[10px] text-spa-light hover:bg-spa-gold hover:text-night-900">FB</a>
|
||||||
)}
|
)}
|
||||||
{settings.instagramUrl && (
|
{settings.instagramUrl && (
|
||||||
<a href={settings.instagramUrl} target="_blank" rel="noreferrer" className="ui-chip h-9 w-9 border-white/10 bg-white/5 text-[10px] text-white hover:bg-spa-gold hover:text-night-900">IG</a>
|
<a href={settings.instagramUrl} target="_blank" rel="noreferrer" className="ui-chip h-9 w-9 border-spa-light bg-spa-light text-[10px] text-spa-light hover:bg-spa-gold hover:text-night-900">IG</a>
|
||||||
)}
|
)}
|
||||||
{settings.tiktokUrl && (
|
{settings.tiktokUrl && (
|
||||||
<a href={settings.tiktokUrl} target="_blank" rel="noreferrer" className="ui-chip h-9 w-9 border-white/10 bg-white/5 text-[10px] text-white hover:bg-spa-gold hover:text-night-900">TT</a>
|
<a href={settings.tiktokUrl} target="_blank" rel="noreferrer" className="ui-chip h-9 w-9 border-spa-light bg-spa-light text-[10px] text-spa-light hover:bg-spa-gold hover:text-night-900">TT</a>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
['IG', 'FB', 'TW', 'YT'].map((s) => (
|
['IG', 'FB', 'TW', 'YT'].map((s) => (
|
||||||
<a key={s} href="#" className="ui-chip h-9 w-9 border-white/10 bg-white/5 text-[10px] text-white hover:bg-spa-gold hover:text-night-900">
|
<a key={s} href="#" className="ui-chip h-9 w-9 border-spa-light bg-spa-light text-[10px] text-spa-light hover:bg-spa-gold hover:text-night-900">
|
||||||
{s}
|
{s}
|
||||||
</a>
|
</a>
|
||||||
))
|
))
|
||||||
@@ -104,30 +104,30 @@ export default function Footer({ categories = [] }: FooterProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h4 className="text-xs font-bold uppercase tracking-widest text-white">{t('footer.explore')}</h4>
|
<h4 className="text-xs font-bold uppercase tracking-widest text-spa-light">{t('footer.explore')}</h4>
|
||||||
<div className="flex flex-col space-y-2 text-sm text-slate-400">
|
<div className="flex flex-col space-y-2 text-sm text-spa-stone">
|
||||||
{exploreMenu.length > 0 ? (
|
{exploreMenu.length > 0 ? (
|
||||||
exploreMenu.map(item => (
|
exploreMenu.map(item => (
|
||||||
<Link key={item.id} to={item.url} className="hover:text-white transition-colors">{translateMenuLabel(item)}</Link>
|
<Link key={item.id} to={item.url} className="hover:text-spa-light transition-colors">{translateMenuLabel(item)}</Link>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Link to="/" className="hover:text-white transition-colors">{t('nav.home')}</Link>
|
<Link to="/" className="hover:text-spa-light transition-colors">{t('nav.home')}</Link>
|
||||||
<Link to="/shop" className="hover:text-white transition-colors">{t('nav.shop')}</Link>
|
<Link to="/shop" className="hover:text-spa-light transition-colors">{t('nav.shop')}</Link>
|
||||||
<Link to="/scent-guide" className="hover:text-white transition-colors">{t('nav.scentGuide')}</Link>
|
<Link to="/scent-guide" className="hover:text-spa-light transition-colors">{t('nav.scentGuide')}</Link>
|
||||||
<Link to="/about" className="hover:text-white transition-colors">{t('nav.about')}</Link>
|
<Link to="/about" className="hover:text-spa-light transition-colors">{t('nav.about')}</Link>
|
||||||
<Link to="/contact" className="hover:text-white transition-colors">{t('nav.contact')}</Link>
|
<Link to="/contact" className="hover:text-spa-light transition-colors">{t('nav.contact')}</Link>
|
||||||
<Link to="/faq" className="hover:text-white transition-colors">{t('nav.faq')}</Link>
|
<Link to="/faq" className="hover:text-spa-light transition-colors">{t('nav.faq')}</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h4 className="text-xs font-bold uppercase tracking-widest text-white">{t('nav.collections')}</h4>
|
<h4 className="text-xs font-bold uppercase tracking-widest text-spa-light">{t('nav.collections')}</h4>
|
||||||
<div className="flex flex-col space-y-2 text-sm text-slate-400">
|
<div className="flex flex-col space-y-2 text-sm text-spa-stone">
|
||||||
{categories.map((c) => (
|
{categories.map((c) => (
|
||||||
<Link key={c.id} to={`/category/${c.id}`} className="hover:text-white transition-colors">
|
<Link key={c.id} to={`/category/${c.id}`} className="hover:text-spa-light transition-colors">
|
||||||
{getLocalizedField(c.name, localStorage.getItem('app_lang') || 'en')}
|
{getLocalizedField(c.name, localStorage.getItem('app_lang') || 'en')}
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
@@ -135,17 +135,17 @@ export default function Footer({ categories = [] }: FooterProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h4 className="text-xs font-bold uppercase tracking-widest text-white">
|
<h4 className="text-xs font-bold uppercase tracking-widest text-spa-light">
|
||||||
{settings?.footerSupportHeading || t('footer.support', 'Support & Legal')}
|
{settings?.footerSupportHeading || t('footer.support', 'Support & Legal')}
|
||||||
</h4>
|
</h4>
|
||||||
<div className="flex flex-col space-y-2 text-sm text-slate-400">
|
<div className="flex flex-col space-y-2 text-sm text-spa-stone">
|
||||||
{supportMenu.map(item => (
|
{supportMenu.map(item => (
|
||||||
<Link key={item.id} to={item.url} className="hover:text-white transition-colors">{translateMenuLabel(item)}</Link>
|
<Link key={item.id} to={item.url} className="hover:text-spa-light transition-colors">{translateMenuLabel(item)}</Link>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<p className="text-[10px] text-slate-600 uppercase tracking-widest">
|
<p className="text-[10px] text-spa-stone uppercase tracking-widest">
|
||||||
{settings?.footerCopyrightText || `© ${new Date().getFullYear()} ${brandName}. ${t('footer.rights')}`}
|
{settings?.footerCopyrightText || `© ${new Date().getFullYear()} ${brandName}. ${t('footer.rights')}`}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -168,10 +168,10 @@ export function MobileDrawer({ isOpen, onClose, items, onOpenLang }: MobileDrawe
|
|||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`absolute top-0 right-0 h-full w-80 max-w-[85vw] bg-white shadow-2xl flex flex-col transition-transform duration-300 ${isOpen ? 'translate-x-0' : 'translate-x-full'}`}
|
className={`absolute top-0 right-0 h-full w-80 max-w-[85vw] bg-spa-light shadow-2xl flex flex-col transition-transform duration-300 ${isOpen ? 'translate-x-0' : 'translate-x-full'}`}
|
||||||
>
|
>
|
||||||
{/* Static Top Header */}
|
{/* Static Top Header */}
|
||||||
<div className="flex items-center justify-between px-6 py-5 border-b border-spa-stone/10 bg-white z-10 shrink-0">
|
<div className="flex items-center justify-between px-6 py-5 border-b border-spa-stone/10 bg-spa-light z-10 shrink-0">
|
||||||
<span className="text-sm font-bold uppercase tracking-widest text-spa-forest">Menu</span>
|
<span className="text-sm font-bold uppercase tracking-widest text-spa-forest">Menu</span>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<button onClick={onOpenLang} className="px-2.5 py-1 rounded-md text-[10px] font-bold text-spa-forest border border-spa-forest/30 uppercase bg-spa-sage flex items-center gap-1 shrink-0">
|
<button onClick={onOpenLang} className="px-2.5 py-1 rounded-md text-[10px] font-bold text-spa-forest border border-spa-forest/30 uppercase bg-spa-sage flex items-center gap-1 shrink-0">
|
||||||
@@ -185,7 +185,7 @@ export function MobileDrawer({ isOpen, onClose, items, onOpenLang }: MobileDrawe
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sliding Views Container */}
|
{/* Sliding Views Container */}
|
||||||
<div className="flex-1 relative overflow-hidden bg-white">
|
<div className="flex-1 relative overflow-hidden bg-spa-light">
|
||||||
{stack.map((view, index) => {
|
{stack.map((view, index) => {
|
||||||
const isCurrent = index === stack.length - 1;
|
const isCurrent = index === stack.length - 1;
|
||||||
const isPast = index < stack.length - 1;
|
const isPast = index < stack.length - 1;
|
||||||
@@ -193,7 +193,7 @@ export function MobileDrawer({ isOpen, onClose, items, onOpenLang }: MobileDrawe
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className={`absolute inset-0 flex flex-col bg-white transition-transform duration-300 ease-in-out ${
|
className={`absolute inset-0 flex flex-col bg-spa-light transition-transform duration-300 ease-in-out ${
|
||||||
isCurrent ? 'translate-x-0' : isPast ? '-translate-x-full' : 'translate-x-full'
|
isCurrent ? 'translate-x-0' : isPast ? '-translate-x-full' : 'translate-x-full'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -225,10 +225,10 @@ export function MobileDrawer({ isOpen, onClose, items, onOpenLang }: MobileDrawe
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Static Bottom Footer */}
|
{/* Static Bottom Footer */}
|
||||||
<div className="px-6 pb-8 pt-6 border-t border-spa-stone/10 bg-white z-10 shrink-0 space-y-3">
|
<div className="px-6 pb-8 pt-6 border-t border-spa-stone/10 bg-spa-light z-10 shrink-0 space-y-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => handleLinkClick('/shop')}
|
onClick={() => handleLinkClick('/shop')}
|
||||||
className="block w-full py-3.5 bg-spa-forest text-white font-bold text-center rounded-xl text-xs uppercase tracking-widest hover:bg-spa-stone transition-colors duration-300 shadow-sm"
|
className="block w-full py-3.5 bg-spa-forest text-spa-light font-bold text-center rounded-xl text-xs uppercase tracking-widest hover:bg-spa-stone transition-colors duration-300 shadow-sm"
|
||||||
>
|
>
|
||||||
Shop Now
|
Shop Now
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
ref={navRef}
|
ref={navRef}
|
||||||
className={`fixed ${finalTopClassName} z-50 w-full border-b border-black/5 transition-all duration-300 ${
|
className={`fixed ${finalTopClassName} z-50 w-full border-b border-black/5 transition-all duration-300 ${
|
||||||
scrolled ? 'shadow-lg shadow-black/5' : 'shadow-none'
|
scrolled ? 'shadow-lg shadow-black/5' : 'shadow-none'
|
||||||
} bg-white/80 px-4 py-3.5 backdrop-blur-xl md:bg-spa-sage md:px-6 md:py-4 md:backdrop-blur-none`}
|
} bg-spa-light px-4 py-3.5 backdrop-blur-xl md:bg-spa-sage md:px-6 md:py-4 md:backdrop-blur-none`}
|
||||||
>
|
>
|
||||||
{/* Single-row layout: Logo (left) | Menu (center, desktop) | Actions (right) */}
|
{/* Single-row layout: Logo (left) | Menu (center, desktop) | Actions (right) */}
|
||||||
<div className="w-full flex items-center justify-between gap-4">
|
<div className="w-full flex items-center justify-between gap-4">
|
||||||
@@ -193,7 +193,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
>
|
>
|
||||||
<Heart className="w-4 h-4 group-hover:text-rose-500 transition-colors" />
|
<Heart className="w-4 h-4 group-hover:text-rose-500 transition-colors" />
|
||||||
{wishlistCount > 0 && (
|
{wishlistCount > 0 && (
|
||||||
<span className="absolute -top-1 -right-1 bg-rose-500 text-white text-[9px] font-bold w-4 h-4 rounded-full flex items-center justify-center">
|
<span className="absolute -top-1 -right-1 bg-rose-500 text-spa-light text-[9px] font-bold w-4 h-4 rounded-full flex items-center justify-center">
|
||||||
{wishlistCount}
|
{wishlistCount}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -206,7 +206,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
>
|
>
|
||||||
<ShoppingBag className="w-4 h-4 group-hover:text-spa-forest transition-colors" />
|
<ShoppingBag className="w-4 h-4 group-hover:text-spa-forest transition-colors" />
|
||||||
{cartCount > 0 && (
|
{cartCount > 0 && (
|
||||||
<span className="absolute -top-1 -right-1 bg-rose-500 text-white text-[9px] font-bold w-4 h-4 rounded-full flex items-center justify-center">
|
<span className="absolute -top-1 -right-1 bg-rose-500 text-spa-light text-[9px] font-bold w-4 h-4 rounded-full flex items-center justify-center">
|
||||||
{cartCount}
|
{cartCount}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -215,12 +215,12 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
{isDesktop && (
|
{isDesktop && (
|
||||||
<>
|
<>
|
||||||
<div className="relative group flex items-center">
|
<div className="relative group flex items-center">
|
||||||
<button className="px-3 py-2 rounded-full text-[10px] font-bold transition-all text-spa-forest border border-spa-forest/30 hover:bg-spa-sage bg-white flex items-center gap-1.5 uppercase tracking-wider">
|
<button className="px-3 py-2 rounded-full text-[10px] font-bold transition-all text-spa-forest border border-spa-forest/30 hover:bg-spa-sage bg-spa-light flex items-center gap-1.5 uppercase tracking-wider">
|
||||||
<Globe className="w-3.5 h-3.5" strokeWidth={1.5} />
|
<Globe className="w-3.5 h-3.5" strokeWidth={1.5} />
|
||||||
{LANGUAGES.find(l => i18n.language.startsWith(l.code))?.short || 'TH'}
|
{LANGUAGES.find(l => i18n.language.startsWith(l.code))?.short || 'TH'}
|
||||||
</button>
|
</button>
|
||||||
<div className="absolute top-full right-0 pt-2 w-36 opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-all duration-300">
|
<div className="absolute top-full right-0 pt-2 w-36 opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-all duration-300">
|
||||||
<div className="bg-white/95 backdrop-blur-xl border border-spa-stone/10 rounded-xl overflow-hidden shadow-2xl p-1.5 space-y-0.5">
|
<div className="bg-spa-light backdrop-blur-xl border border-spa-stone/10 rounded-xl overflow-hidden shadow-2xl p-1.5 space-y-0.5">
|
||||||
{LANGUAGES.map(lang => {
|
{LANGUAGES.map(lang => {
|
||||||
const isActive = i18n.language.startsWith(lang.code)
|
const isActive = i18n.language.startsWith(lang.code)
|
||||||
return (
|
return (
|
||||||
@@ -248,7 +248,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="absolute top-full right-0 pt-3 w-36 opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-all duration-300">
|
<div className="absolute top-full right-0 pt-3 w-36 opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto transition-all duration-300">
|
||||||
<div className="bg-white/95 backdrop-blur-xl border border-spa-stone/10 rounded-xl overflow-hidden shadow-xl p-1">
|
<div className="bg-spa-light backdrop-blur-xl border border-spa-stone/10 rounded-xl overflow-hidden shadow-xl p-1">
|
||||||
<div className="px-3 py-2 border-b border-spa-stone/10 mb-1">
|
<div className="px-3 py-2 border-b border-spa-stone/10 mb-1">
|
||||||
<p className="text-[10px] text-spa-stone/60 uppercase tracking-widest leading-tight truncate">{t('nav.signedInAs', 'Signed in as')}</p>
|
<p className="text-[10px] text-spa-stone/60 uppercase tracking-widest leading-tight truncate">{t('nav.signedInAs', 'Signed in as')}</p>
|
||||||
<p className="text-xs text-spa-forest font-bold truncate">{customer.displayName}</p>
|
<p className="text-xs text-spa-forest font-bold truncate">{customer.displayName}</p>
|
||||||
@@ -271,7 +271,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={loginLiff}
|
onClick={loginLiff}
|
||||||
className="ml-2 px-4 py-2 rounded-full text-xs font-bold uppercase tracking-widest bg-[#00B900] text-white hover:bg-[#009900] transition-all duration-300 shadow-sm"
|
className="ml-2 px-4 py-2 rounded-full text-xs font-bold uppercase tracking-widest bg-[#00B900] text-spa-light hover:bg-[#009900] transition-all duration-300 shadow-sm"
|
||||||
>
|
>
|
||||||
{t('nav.login', 'LINE Login')}
|
{t('nav.login', 'LINE Login')}
|
||||||
</button>
|
</button>
|
||||||
@@ -317,7 +317,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleSearch}>
|
<form onSubmit={handleSearch}>
|
||||||
<div className="flex bg-white border-2 border-spa-stone/20 focus-within:border-spa-forest rounded-2xl overflow-hidden transition-colors shadow-sm">
|
<div className="flex bg-spa-light border-2 border-spa-stone/20 focus-within:border-spa-forest rounded-2xl overflow-hidden transition-colors shadow-sm">
|
||||||
<svg className="w-5 h-5 text-spa-stone/50 m-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-5 h-5 text-spa-stone/50 m-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -329,7 +329,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
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"
|
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
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<button type="submit" className="bg-spa-forest text-white px-4 md:px-6 font-bold text-xs uppercase tracking-widest shrink-0 hover:bg-spa-stone transition-colors">
|
<button type="submit" className="bg-spa-forest text-spa-light px-4 md:px-6 font-bold text-xs uppercase tracking-widest shrink-0 hover:bg-spa-stone transition-colors">
|
||||||
Go
|
Go
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -346,7 +346,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
|||||||
onClick={() => setLangSheetOpen(false)}
|
onClick={() => setLangSheetOpen(false)}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`absolute bottom-0 left-0 right-0 bg-white rounded-t-3xl transition-transform duration-300 shadow-2xl pb-safe ${langSheetOpen ? 'translate-y-0' : 'translate-y-full'}`}
|
className={`absolute bottom-0 left-0 right-0 bg-spa-light rounded-t-3xl transition-transform duration-300 shadow-2xl pb-safe ${langSheetOpen ? 'translate-y-0' : 'translate-y-full'}`}
|
||||||
>
|
>
|
||||||
<div className="flex justify-center pt-3 pb-1">
|
<div className="flex justify-center pt-3 pb-1">
|
||||||
<div className="w-12 h-1.5 bg-spa-stone/20 rounded-full" />
|
<div className="w-12 h-1.5 bg-spa-stone/20 rounded-full" />
|
||||||
|
|||||||
@@ -57,13 +57,13 @@ export default function ProductCard({ product, enableQuickShop = false, category
|
|||||||
(e.target as HTMLImageElement).style.display = 'none';
|
(e.target as HTMLImageElement).style.display = 'none';
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="ui-chip absolute left-3 top-3 border-white/20 bg-white/90 px-2.5 py-1 text-[10px] text-spa-forest shadow-sm backdrop-blur-sm">
|
<div className="ui-chip absolute left-3 top-3 border-spa-light bg-spa-light px-2.5 py-1 text-[10px] text-spa-forest shadow-sm backdrop-blur-sm">
|
||||||
{displayCategoryName}
|
{displayCategoryName}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleWishlistClick}
|
onClick={handleWishlistClick}
|
||||||
className="absolute right-3 top-3 z-10 rounded-full bg-white/90 p-2 shadow-sm backdrop-blur-sm transition-transform hover:scale-110 cursor-pointer"
|
className="absolute right-3 top-3 z-10 rounded-full bg-spa-light p-2 shadow-sm backdrop-blur-sm transition-transform hover:scale-110 cursor-pointer"
|
||||||
>
|
>
|
||||||
<Heart
|
<Heart
|
||||||
className={`w-4 h-4 transition-colors ${isWished ? 'fill-rose-500 text-rose-500' : 'text-spa-stone/60 hover:text-rose-500'}`}
|
className={`w-4 h-4 transition-colors ${isWished ? 'fill-rose-500 text-rose-500' : 'text-spa-stone/60 hover:text-rose-500'}`}
|
||||||
@@ -96,8 +96,8 @@ export default function ProductCard({ product, enableQuickShop = false, category
|
|||||||
}}
|
}}
|
||||||
className={`rounded-full border px-3 py-1 text-[10px] font-bold uppercase tracking-wider transition-all cursor-pointer ${
|
className={`rounded-full border px-3 py-1 text-[10px] font-bold uppercase tracking-wider transition-all cursor-pointer ${
|
||||||
isSelected
|
isSelected
|
||||||
? 'border-spa-forest bg-spa-forest text-white shadow-sm'
|
? 'border-spa-forest bg-spa-forest text-spa-light shadow-sm'
|
||||||
: 'border-spa-stone/10 bg-white text-spa-stone hover:border-spa-forest/30'
|
: 'border-spa-stone/10 bg-spa-light text-spa-stone hover:border-spa-forest/30'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{variant.size}
|
{variant.size}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export default function ProductImageGallery({ images, fallback, alt = 'Product I
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{/* Main Image */}
|
{/* Main Image */}
|
||||||
<div className="relative rounded-2xl overflow-hidden h-[480px] group bg-white shadow-sm border border-spa-stone/10 flex items-center justify-center">
|
<div className="relative rounded-2xl overflow-hidden h-[480px] group bg-spa-light shadow-sm border border-spa-stone/10 flex items-center justify-center">
|
||||||
<img
|
<img
|
||||||
src={currentImageUrl}
|
src={currentImageUrl}
|
||||||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-700"
|
||||||
@@ -38,7 +38,7 @@ export default function ProductImageGallery({ images, fallback, alt = 'Product I
|
|||||||
<div className="absolute inset-0 bg-gradient-to-t from-black/10 to-transparent pointer-events-none" />
|
<div className="absolute inset-0 bg-gradient-to-t from-black/10 to-transparent pointer-events-none" />
|
||||||
|
|
||||||
{badgeText && (
|
{badgeText && (
|
||||||
<div className="absolute top-6 left-6 bg-white/90 backdrop-blur-sm px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest text-spa-forest shadow-sm pointer-events-none">
|
<div className="absolute top-6 left-6 bg-spa-light backdrop-blur-sm px-3 py-1.5 rounded-full text-[10px] font-bold uppercase tracking-widest text-spa-forest shadow-sm pointer-events-none">
|
||||||
{badgeText}
|
{badgeText}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -48,7 +48,7 @@ export default function ProductImageGallery({ images, fallback, alt = 'Product I
|
|||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
onClick={handlePrev}
|
onClick={handlePrev}
|
||||||
className="absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-white/80 backdrop-blur-md rounded-full flex items-center justify-center text-spa-forest opacity-0 group-hover:opacity-100 transition-opacity hover:bg-white hover:scale-110 shadow-sm"
|
className="absolute left-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-spa-light backdrop-blur-md rounded-full flex items-center justify-center text-spa-forest opacity-0 group-hover:opacity-100 transition-opacity hover:bg-spa-light hover:scale-110 shadow-sm"
|
||||||
aria-label="Previous image"
|
aria-label="Previous image"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
@@ -57,7 +57,7 @@ export default function ProductImageGallery({ images, fallback, alt = 'Product I
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-white/80 backdrop-blur-md rounded-full flex items-center justify-center text-spa-forest opacity-0 group-hover:opacity-100 transition-opacity hover:bg-white hover:scale-110 shadow-sm"
|
className="absolute right-4 top-1/2 -translate-y-1/2 w-10 h-10 bg-spa-light backdrop-blur-md rounded-full flex items-center justify-center text-spa-forest opacity-0 group-hover:opacity-100 transition-opacity hover:bg-spa-light hover:scale-110 shadow-sm"
|
||||||
aria-label="Next image"
|
aria-label="Next image"
|
||||||
>
|
>
|
||||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
|||||||
{/* List & Form */}
|
{/* List & Form */}
|
||||||
<div className="w-full md:w-2/3 space-y-8">
|
<div className="w-full md:w-2/3 space-y-8">
|
||||||
{customer ? (
|
{customer ? (
|
||||||
<form onSubmit={handleSubmit} className="bg-white p-6 rounded-3xl border border-spa-stone/10 shadow-sm space-y-4">
|
<form onSubmit={handleSubmit} className="bg-spa-light p-6 rounded-3xl border border-spa-stone/10 shadow-sm space-y-4">
|
||||||
<h3 className="font-bold text-pop-black">Write a Review</h3>
|
<h3 className="font-bold text-pop-black">Write a Review</h3>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{[1, 2, 3, 4, 5].map(star => (
|
{[1, 2, 3, 4, 5].map(star => (
|
||||||
@@ -115,7 +115,7 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
className="px-6 py-2.5 bg-spa-forest text-white text-sm font-bold tracking-widest uppercase rounded-full hover:bg-spa-stone transition-all disabled:opacity-50"
|
className="px-6 py-2.5 bg-spa-forest text-spa-light text-sm font-bold tracking-widest uppercase rounded-full hover:bg-spa-stone transition-all disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{submitting ? 'Submitting...' : 'Submit Review'}
|
{submitting ? 'Submitting...' : 'Submit Review'}
|
||||||
</button>
|
</button>
|
||||||
@@ -135,8 +135,8 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
|||||||
onClick={() => { setFilterRating(null); setVisibleCount(3); }}
|
onClick={() => { setFilterRating(null); setVisibleCount(3); }}
|
||||||
className={`flex-shrink-0 px-4 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
className={`flex-shrink-0 px-4 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
||||||
filterRating === null
|
filterRating === null
|
||||||
? 'bg-spa-stone text-white border-spa-stone'
|
? 'bg-spa-stone text-spa-light border-spa-stone'
|
||||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-stone/50'
|
: 'bg-spa-light text-spa-stone border-spa-stone/20 hover:border-spa-stone/50'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
All ({reviews.length})
|
All ({reviews.length})
|
||||||
@@ -150,11 +150,11 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
|||||||
onClick={() => { setFilterRating(star); setVisibleCount(3); }}
|
onClick={() => { setFilterRating(star); setVisibleCount(3); }}
|
||||||
className={`flex-shrink-0 flex items-center gap-1 px-4 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
className={`flex-shrink-0 flex items-center gap-1 px-4 py-1.5 rounded-full text-xs font-bold transition-all border ${
|
||||||
filterRating === star
|
filterRating === star
|
||||||
? 'bg-spa-stone text-white border-spa-stone'
|
? 'bg-spa-stone text-spa-light border-spa-stone'
|
||||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-stone/50'
|
: 'bg-spa-light text-spa-stone border-spa-stone/20 hover:border-spa-stone/50'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{star} <Star className={`w-3 h-3 ${filterRating === star ? 'fill-white text-white' : 'fill-orange-400 text-orange-400'}`} /> ({count})
|
{star} <Star className={`w-3 h-3 ${filterRating === star ? 'fill-white text-spa-light' : 'fill-orange-400 text-orange-400'}`} /> ({count})
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -174,7 +174,7 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
|||||||
{review.customer?.pictureUrl ? (
|
{review.customer?.pictureUrl ? (
|
||||||
<img src={review.customer.pictureUrl} className="w-10 h-10 rounded-full object-cover" />
|
<img src={review.customer.pictureUrl} className="w-10 h-10 rounded-full object-cover" />
|
||||||
) : (
|
) : (
|
||||||
<div className="w-10 h-10 rounded-full bg-spa-stone/20 flex items-center justify-center text-white font-bold">
|
<div className="w-10 h-10 rounded-full bg-spa-stone/20 flex items-center justify-center text-spa-light font-bold">
|
||||||
{(review.customerName || 'A')[0].toUpperCase()}
|
{(review.customerName || 'A')[0].toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -197,7 +197,7 @@ export default function ProductReviews({ productId }: { productId: number }) {
|
|||||||
<div className="text-center pt-4">
|
<div className="text-center pt-4">
|
||||||
<button
|
<button
|
||||||
onClick={() => setVisibleCount(prev => prev + 5)}
|
onClick={() => setVisibleCount(prev => prev + 5)}
|
||||||
className="px-6 py-2.5 border border-spa-stone/20 text-spa-stone text-xs font-bold tracking-widest uppercase rounded-full hover:border-spa-stone hover:text-pop-black transition-all bg-white"
|
className="px-6 py-2.5 border border-spa-stone/20 text-spa-stone text-xs font-bold tracking-widest uppercase rounded-full hover:border-spa-stone hover:text-pop-black transition-all bg-spa-light"
|
||||||
>
|
>
|
||||||
Show More Reviews
|
Show More Reviews
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export default function ContactFormSection({ payload }: ContactFormSectionProps)
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white rounded-3xl shadow-xl shadow-spa-sand/50 p-8 md:p-12 border border-spa-sage/10">
|
<div className="bg-spa-light rounded-3xl shadow-xl shadow-spa-sand/50 p-8 md:p-12 border border-spa-sage/10">
|
||||||
{isSuccess ? (
|
{isSuccess ? (
|
||||||
<div className="text-center py-12">
|
<div className="text-center py-12">
|
||||||
<div className="w-16 h-16 bg-green-100 text-green-600 rounded-full flex items-center justify-center mx-auto mb-6">
|
<div className="w-16 h-16 bg-green-100 text-green-600 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||||
@@ -192,10 +192,10 @@ export default function ContactFormSection({ payload }: ContactFormSectionProps)
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
className="w-full py-4 px-6 bg-spa-sage hover:bg-spa-sage-dark text-white rounded-xl font-medium flex items-center justify-center gap-2 transition-colors disabled:opacity-70 disabled:cursor-not-allowed"
|
className="w-full py-4 px-6 bg-spa-sage hover:bg-spa-sage-dark text-spa-light rounded-xl font-medium flex items-center justify-center gap-2 transition-colors disabled:opacity-70 disabled:cursor-not-allowed"
|
||||||
>
|
>
|
||||||
{isSubmitting ? (
|
{isSubmitting ? (
|
||||||
<span className="w-6 h-6 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
<span className="w-6 h-6 border-2 border-spa-light border-t-white rounded-full animate-spin" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Send size={18} />
|
<Send size={18} />
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default function ContactInfoSection({ payload }: ContactInfoSectionProps)
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="ui-section-band w-full bg-white px-4 md:px-8">
|
<section className="ui-section-band w-full bg-spa-light px-4 md:px-8">
|
||||||
<div className="ui-section-inner">
|
<div className="ui-section-inner">
|
||||||
{title && (
|
{title && (
|
||||||
<div className="mb-10 text-center">
|
<div className="mb-10 text-center">
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ export function ThaiAddressInput({ value, onChange }: ThaiAddressInputProps) {
|
|||||||
if (focusedField !== field || results.length === 0) return null;
|
if (focusedField !== field || results.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ul className="absolute z-50 w-full bg-white border border-spa-stone/20 rounded-lg mt-1 max-h-60 overflow-y-auto shadow-xl">
|
<ul className="absolute z-50 w-full bg-spa-light border border-spa-stone/20 rounded-lg mt-1 max-h-60 overflow-y-auto shadow-xl">
|
||||||
{results.map((item, idx) => (
|
{results.map((item, idx) => (
|
||||||
<li
|
<li
|
||||||
key={`${item.district}-${item.amphoe}-${item.province}-${item.zipcode}-${idx}`}
|
key={`${item.district}-${item.amphoe}-${item.province}-${item.zipcode}-${idx}`}
|
||||||
|
|||||||
+18
-18
@@ -99,11 +99,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-section-subtitle {
|
.ui-section-subtitle {
|
||||||
@apply mx-auto max-w-2xl text-gray-500;
|
@apply mx-auto max-w-2xl text-spa-stone;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-shell-card {
|
.ui-shell-card {
|
||||||
@apply rounded-3xl border border-spa-stone/10 bg-white shadow-brutal transition-all duration-300;
|
@apply rounded-3xl border border-spa-stone/10 bg-spa-light shadow-brutal transition-all duration-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-shell-card-hover {
|
.ui-shell-card-hover {
|
||||||
@@ -115,15 +115,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-button-primary {
|
.ui-button-primary {
|
||||||
@apply inline-flex items-center justify-center rounded-full bg-spa-forest px-6 py-3 text-xs font-bold uppercase tracking-widest text-white transition-colors hover:bg-spa-forest/90;
|
@apply inline-flex items-center justify-center rounded-full bg-spa-forest px-6 py-3 text-xs font-bold uppercase tracking-widest text-spa-light transition-colors hover:bg-spa-forest/90;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-button-secondary {
|
.ui-button-secondary {
|
||||||
@apply inline-flex items-center justify-center rounded-full bg-white px-6 py-3 text-xs font-bold uppercase tracking-widest text-spa-forest transition-colors hover:bg-spa-sage;
|
@apply inline-flex items-center justify-center rounded-full bg-spa-light px-6 py-3 text-xs font-bold uppercase tracking-widest text-spa-forest transition-colors hover:bg-spa-sage;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-input-shell {
|
.ui-input-shell {
|
||||||
@apply w-full rounded-full border border-white/10 bg-white/5 px-6 py-4 text-sm text-white outline-none transition placeholder:text-white/40 focus:border-spa-gold/80;
|
@apply w-full rounded-full border border-spa-light bg-spa-light px-6 py-4 text-sm text-spa-light outline-none transition placeholder:text-spa-light focus:border-spa-gold/80;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-muted-text {
|
.ui-muted-text {
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-hero-shell {
|
.ui-hero-shell {
|
||||||
@apply relative flex min-h-[90vh] items-center justify-center overflow-hidden bg-night-900 text-white md:aspect-[21/9] md:min-h-0;
|
@apply relative flex min-h-[90vh] items-center justify-center overflow-hidden bg-night-900 text-spa-light md:aspect-[21/9] md:min-h-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-hero-overlay {
|
.ui-hero-overlay {
|
||||||
@@ -147,11 +147,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-hero-title {
|
.ui-hero-title {
|
||||||
@apply font-serif text-5xl font-light leading-tight tracking-wide text-white drop-shadow-md md:text-7xl lg:text-8xl;
|
@apply font-serif text-5xl font-light leading-tight tracking-wide text-spa-light drop-shadow-md md:text-7xl lg:text-8xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-hero-copy {
|
.ui-hero-copy {
|
||||||
@apply mx-auto max-w-2xl pb-8 pt-4 text-lg font-light text-white/90 drop-shadow-md md:text-xl;
|
@apply mx-auto max-w-2xl pb-8 pt-4 text-lg font-light text-spa-light drop-shadow-md md:text-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-banner-shell {
|
.ui-banner-shell {
|
||||||
@@ -163,19 +163,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-banner-content {
|
.ui-banner-content {
|
||||||
@apply relative z-10 max-w-xl space-y-4 p-8 text-white md:p-16;
|
@apply relative z-10 max-w-xl space-y-4 p-8 text-spa-light md:p-16;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-card-soft {
|
.ui-card-soft {
|
||||||
@apply rounded-2xl border border-spa-stone/10 bg-white shadow-sm;
|
@apply rounded-2xl border border-spa-stone/10 bg-spa-light shadow-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dark-shell {
|
.ui-dark-shell {
|
||||||
@apply bg-spa-forest px-6 py-20 text-white;
|
@apply bg-spa-forest px-6 py-20 text-spa-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dark-card {
|
.ui-dark-card {
|
||||||
@apply overflow-hidden rounded-3xl border border-white/5 bg-white/5;
|
@apply overflow-hidden rounded-3xl border border-spa-light bg-spa-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dark-card-title {
|
.ui-dark-card-title {
|
||||||
@@ -187,11 +187,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-dark-link {
|
.ui-dark-link {
|
||||||
@apply inline-flex items-center gap-2 text-xs font-bold uppercase tracking-widest text-spa-gold transition-colors hover:text-white;
|
@apply inline-flex items-center gap-2 text-xs font-bold uppercase tracking-widest text-spa-gold transition-colors hover:text-spa-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brutal-box {
|
.brutal-box {
|
||||||
@apply bg-white border border-gray-100 rounded-2xl shadow-brutal transition-all duration-300;
|
@apply bg-spa-light border border-spa-stone/20 rounded-2xl shadow-brutal transition-all duration-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brutal-box-hover {
|
.brutal-box-hover {
|
||||||
@@ -199,11 +199,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.brutal-button {
|
.brutal-button {
|
||||||
@apply px-8 py-4 bg-pop-pink text-white font-medium rounded-full shadow-brutal transition-all duration-300 hover:bg-pop-red hover:-translate-y-1 hover:shadow-brutal-hover;
|
@apply px-8 py-4 bg-pop-pink text-spa-light font-medium rounded-full shadow-brutal transition-all duration-300 hover:bg-pop-red hover:-translate-y-1 hover:shadow-brutal-hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brutal-button-alt {
|
.brutal-button-alt {
|
||||||
@apply px-8 py-4 bg-white text-pop-pink font-medium rounded-full border border-pop-pink shadow-brutal transition-all duration-300 hover:bg-pop-bg hover:-translate-y-1 hover:shadow-brutal-hover;
|
@apply px-8 py-4 bg-spa-light text-pop-pink font-medium rounded-full border border-pop-pink shadow-brutal transition-all duration-300 hover:bg-pop-bg hover:-translate-y-1 hover:shadow-brutal-hover;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,11 +217,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
@apply bg-gray-300 rounded-full;
|
@apply bg-spa-sage rounded-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
@apply bg-gray-400;
|
@apply bg-spa-sage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smooth Scrolling */
|
/* Smooth Scrolling */
|
||||||
|
|||||||
@@ -49,13 +49,13 @@ export default function ArticleReaderPage() {
|
|||||||
<main className="flex-1 pt-24 pb-20">
|
<main className="flex-1 pt-24 pb-20">
|
||||||
<article className="max-w-4xl mx-auto w-full animate-fade-in relative mt-8">
|
<article className="max-w-4xl mx-auto w-full animate-fade-in relative mt-8">
|
||||||
|
|
||||||
<Link to="/scent-guide" className="absolute -left-16 xl:-left-24 top-0 mt-8 hidden lg:flex p-4 bg-white shadow-sm border border-spa-stone/10 border border-spa-stone/10 rounded-full hover:bg-white/10 transition-colors">
|
<Link to="/scent-guide" className="absolute -left-16 xl:-left-24 top-0 mt-8 hidden lg:flex p-4 bg-spa-light shadow-sm border border-spa-stone/10 border border-spa-stone/10 rounded-full hover:bg-spa-light transition-colors">
|
||||||
<ArrowLeft className="w-5 h-5 text-slate-300" />
|
<ArrowLeft className="w-5 h-5 text-spa-stone" />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="text-center px-6 py-12 space-y-6">
|
<div className="text-center px-6 py-12 space-y-6">
|
||||||
<Link to="/scent-guide" className="lg:hidden text-slate-400 text-sm hover:text-white inline-flex items-center gap-2 mb-4">
|
<Link to="/scent-guide" className="lg:hidden text-spa-stone text-sm hover:text-spa-light inline-flex items-center gap-2 mb-4">
|
||||||
<ArrowLeft className="w-4 h-4" /> Go Back
|
<ArrowLeft className="w-4 h-4" /> Go Back
|
||||||
</Link>
|
</Link>
|
||||||
<p className="text-spa-forest uppercase tracking-[0.3em] font-bold text-xs">
|
<p className="text-spa-forest uppercase tracking-[0.3em] font-bold text-xs">
|
||||||
@@ -68,7 +68,7 @@ export default function ArticleReaderPage() {
|
|||||||
|
|
||||||
{/* Cover Image */}
|
{/* Cover Image */}
|
||||||
{article.coverImage && (
|
{article.coverImage && (
|
||||||
<div className="w-full aspect-[16/7] bg-white md:rounded-[2rem] overflow-hidden shadow-2xl mb-16 border border-spa-stone/10">
|
<div className="w-full aspect-[16/7] bg-spa-light md:rounded-[2rem] overflow-hidden shadow-2xl mb-16 border border-spa-stone/10">
|
||||||
<img src={article.coverImage} alt={article.title} className="w-full h-full object-cover" />
|
<img src={article.coverImage} alt={article.title} className="w-full h-full object-cover" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -132,15 +132,15 @@ export default function CategoryPage() {
|
|||||||
<div className="max-w-7xl mx-auto">
|
<div className="max-w-7xl mx-auto">
|
||||||
{/* Breadcrumb */}
|
{/* Breadcrumb */}
|
||||||
<div className="flex items-center gap-2 text-xs text-spa-sage/80 uppercase tracking-widest mb-4 font-semibold">
|
<div className="flex items-center gap-2 text-xs text-spa-sage/80 uppercase tracking-widest mb-4 font-semibold">
|
||||||
<Link to="/" className="hover:text-white transition-colors">Home</Link>
|
<Link to="/" className="hover:text-spa-light transition-colors">Home</Link>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
<Link to="/shop" className="hover:text-white transition-colors">Shop</Link>
|
<Link to="/shop" className="hover:text-spa-light transition-colors">Shop</Link>
|
||||||
<span>/</span>
|
<span>/</span>
|
||||||
<span className="text-white">{typeof category?.name === 'string' ? category.name : (category?.name as any)?.[localStorage.getItem('app_lang') || 'en'] || (category?.name as any)?.en || '...'}</span>
|
<span className="text-spa-light">{typeof category?.name === 'string' ? category.name : (category?.name as any)?.[localStorage.getItem('app_lang') || 'en'] || (category?.name as any)?.en || '...'}</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-4xl md:text-5xl font-bold text-white">
|
<h1 className="text-4xl md:text-5xl font-bold text-spa-light">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div className="h-10 w-48 bg-white/20 animate-pulse rounded-lg" />
|
<div className="h-10 w-48 bg-spa-light animate-pulse rounded-lg" />
|
||||||
) : (
|
) : (
|
||||||
typeof category?.name === 'string' ? category.name : (category?.name as any)?.[i18n.language] || (category?.name as any)?.en || ''
|
typeof category?.name === 'string' ? category.name : (category?.name as any)?.[i18n.language] || (category?.name as any)?.en || ''
|
||||||
)}
|
)}
|
||||||
@@ -165,8 +165,8 @@ export default function CategoryPage() {
|
|||||||
to={`/category/${cat.id}`}
|
to={`/category/${cat.id}`}
|
||||||
className={`px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-widest transition-all duration-300 border ${
|
className={`px-4 py-1.5 rounded-full text-xs font-bold uppercase tracking-widest transition-all duration-300 border ${
|
||||||
cat.id === category?.id
|
cat.id === category?.id
|
||||||
? 'bg-spa-forest text-white border-spa-forest shadow-md'
|
? 'bg-spa-forest text-spa-light border-spa-forest shadow-md'
|
||||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
: 'bg-spa-light text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[localStorage.getItem('app_lang') || 'en'] || (cat.name as any)?.en || ''}
|
{typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[localStorage.getItem('app_lang') || 'en'] || (cat.name as any)?.en || ''}
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ export default function CheckoutPage() {
|
|||||||
{/* Form Section */}
|
{/* Form Section */}
|
||||||
<div className="flex-1 space-y-8 order-2 lg:order-1">
|
<div className="flex-1 space-y-8 order-2 lg:order-1">
|
||||||
<form id="checkout-form" onSubmit={handleSubmit} className="space-y-6">
|
<form id="checkout-form" onSubmit={handleSubmit} className="space-y-6">
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 space-y-6 shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 space-y-6 shadow-sm">
|
||||||
<h2 className="text-xl font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-4">{t('checkout.shippingInfo')}</h2>
|
<h2 className="text-xl font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-4">{t('checkout.shippingInfo')}</h2>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
<div className="grid md:grid-cols-2 gap-6">
|
||||||
@@ -433,20 +433,20 @@ export default function CheckoutPage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading || loadingSettings}
|
disabled={loading || loadingSettings}
|
||||||
className="w-full py-4 px-6 bg-spa-forest text-white font-bold rounded-xl tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed flex lg:hidden justify-between items-center mt-4"
|
className="w-full py-4 px-6 bg-spa-forest text-spa-light font-bold rounded-xl tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed flex lg:hidden justify-between items-center mt-4"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{loading && <Loader2 className="w-5 h-5 animate-spin" />}
|
{loading && <Loader2 className="w-5 h-5 animate-spin" />}
|
||||||
<span>Proceed to Payment</span>
|
<span>Proceed to Payment</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm border-l border-white/20 pl-4">{formatPrice(total)}</span>
|
<span className="text-sm border-l border-spa-light pl-4">{formatPrice(total)}</span>
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Summary Section */}
|
{/* Summary Section */}
|
||||||
<div className="lg:w-96 space-y-6 order-1 lg:order-2">
|
<div className="lg:w-96 space-y-6 order-1 lg:order-2">
|
||||||
<div className="bg-white p-6 lg:p-8 rounded-2xl border border-spa-stone/10 shadow-sm sticky top-32 transition-all">
|
<div className="bg-spa-light p-6 lg:p-8 rounded-2xl border border-spa-stone/10 shadow-sm sticky top-32 transition-all">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsSummaryExpanded(!isSummaryExpanded)}
|
onClick={() => setIsSummaryExpanded(!isSummaryExpanded)}
|
||||||
@@ -602,13 +602,13 @@ export default function CheckoutPage() {
|
|||||||
form="checkout-form"
|
form="checkout-form"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading || loadingSettings}
|
disabled={loading || loadingSettings}
|
||||||
className="w-full mt-8 py-4 px-6 bg-spa-forest text-white font-bold rounded-xl tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed hidden lg:flex justify-between items-center"
|
className="w-full mt-8 py-4 px-6 bg-spa-forest text-spa-light font-bold rounded-xl tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed hidden lg:flex justify-between items-center"
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{loading && <Loader2 className="w-5 h-5 animate-spin" />}
|
{loading && <Loader2 className="w-5 h-5 animate-spin" />}
|
||||||
<span>Proceed to Payment</span>
|
<span>Proceed to Payment</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-sm border-l border-white/20 pl-4">{formatPrice(total)}</span>
|
<span className="text-sm border-l border-spa-light pl-4">{formatPrice(total)}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export default function CouponsPage() {
|
|||||||
{coupons.map(coupon => (
|
{coupons.map(coupon => (
|
||||||
<div
|
<div
|
||||||
key={coupon.id}
|
key={coupon.id}
|
||||||
className="group relative bg-white border border-spa-stone/10 rounded-3xl overflow-hidden hover:border-spa-forest transition-all duration-500 shadow-sm hover:shadow-md"
|
className="group relative bg-spa-light border border-spa-stone/10 rounded-3xl overflow-hidden hover:border-spa-forest transition-all duration-500 shadow-sm hover:shadow-md"
|
||||||
>
|
>
|
||||||
{/* Decoration */}
|
{/* Decoration */}
|
||||||
<div className="absolute top-0 right-0 w-32 h-32 bg-spa-sage/20 blur-3xl rounded-full -translate-y-1/2 translate-x-1/2" />
|
<div className="absolute top-0 right-0 w-32 h-32 bg-spa-sage/20 blur-3xl rounded-full -translate-y-1/2 translate-x-1/2" />
|
||||||
@@ -184,7 +184,7 @@ export default function CouponsPage() {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={() => handleUseNow(coupon.code)}
|
onClick={() => handleUseNow(coupon.code)}
|
||||||
className="w-full py-4 bg-spa-forest text-white hover:bg-spa-stone font-bold rounded-xl flex items-center justify-center gap-2 transition-all duration-300 shadow-sm"
|
className="w-full py-4 bg-spa-forest text-spa-light hover:bg-spa-stone font-bold rounded-xl flex items-center justify-center gap-2 transition-all duration-300 shadow-sm"
|
||||||
>
|
>
|
||||||
ใช้คูปองนี้เลย
|
ใช้คูปองนี้เลย
|
||||||
<ArrowRight className="w-4 h-4" />
|
<ArrowRight className="w-4 h-4" />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Link } from 'react-router-dom'
|
|||||||
|
|
||||||
export default function NotFoundPage() {
|
export default function NotFoundPage() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex items-center justify-center px-6 bg-white">
|
<div className="min-h-screen flex items-center justify-center px-6 bg-spa-light">
|
||||||
{/* Background glow */}
|
{/* Background glow */}
|
||||||
<div className="absolute inset-0 pointer-events-none overflow-hidden">
|
<div className="absolute inset-0 pointer-events-none overflow-hidden">
|
||||||
<div className="absolute top-1/3 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-spa-forest/10 rounded-full blur-3xl" />
|
<div className="absolute top-1/3 left-1/2 -translate-x-1/2 -translate-y-1/2 w-96 h-96 bg-spa-forest/10 rounded-full blur-3xl" />
|
||||||
@@ -22,7 +22,7 @@ export default function NotFoundPage() {
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<h1 className="text-3xl font-bold">Page Not Found</h1>
|
<h1 className="text-3xl font-bold">Page Not Found</h1>
|
||||||
<p className="text-slate-400 leading-relaxed">
|
<p className="text-spa-stone leading-relaxed">
|
||||||
The page you're looking for seems to have drifted away like morning mist. Let's guide you back to serenity.
|
The page you're looking for seems to have drifted away like morning mist. Let's guide you back to serenity.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -30,23 +30,23 @@ export default function NotFoundPage() {
|
|||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-2">
|
<div className="flex flex-col sm:flex-row gap-4 justify-center pt-2">
|
||||||
<Link
|
<Link
|
||||||
to="/"
|
to="/"
|
||||||
className="px-8 py-3.5 bg-white text-spa-forest font-bold rounded-lg tracking-widest uppercase hover:bg-spa-forest text-white hover:shadow-[0_0_25px_rgba(102,252,241,0.4)] transition-all duration-500 text-sm"
|
className="px-8 py-3.5 bg-spa-light text-spa-forest font-bold rounded-lg tracking-widest uppercase hover:bg-spa-forest text-spa-light hover:shadow-[0_0_25px_rgba(102,252,241,0.4)] transition-all duration-500 text-sm"
|
||||||
>
|
>
|
||||||
Go Home
|
Go Home
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to="/shop"
|
to="/shop"
|
||||||
className="px-8 py-3.5 bg-white shadow-sm border border-spa-stone/10 text-white font-bold rounded-lg tracking-widest uppercase hover:bg-white/10 transition-all duration-300 border border-spa-stone/10 text-sm"
|
className="px-8 py-3.5 bg-spa-light shadow-sm border border-spa-stone/10 text-spa-light font-bold rounded-lg tracking-widest uppercase hover:bg-spa-light transition-all duration-300 border border-spa-stone/10 text-sm"
|
||||||
>
|
>
|
||||||
Browse Products
|
Browse Products
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="pt-4 border-t border-spa-stone/10">
|
<div className="pt-4 border-t border-spa-stone/10">
|
||||||
<div className="flex justify-center gap-6 text-xs text-slate-500">
|
<div className="flex justify-center gap-6 text-xs text-spa-stone">
|
||||||
<Link to="/scent-guide" className="hover:text-white transition-colors">Scent Guide</Link>
|
<Link to="/scent-guide" className="hover:text-spa-light transition-colors">Scent Guide</Link>
|
||||||
<Link to="/faq" className="hover:text-white transition-colors">FAQ</Link>
|
<Link to="/faq" className="hover:text-spa-light transition-colors">FAQ</Link>
|
||||||
<Link to="/contact" className="hover:text-white transition-colors">Contact</Link>
|
<Link to="/contact" className="hover:text-spa-light transition-colors">Contact</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -258,7 +258,7 @@ export default function OrderSuccessPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Order Summary */}
|
{/* Order Summary */}
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 space-y-6 shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 space-y-6 shadow-sm">
|
||||||
<h2 className="text-sm font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-4">Order Items</h2>
|
<h2 className="text-sm font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-4">Order Items</h2>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
{order.items.map(item => (
|
{order.items.map(item => (
|
||||||
@@ -293,13 +293,13 @@ export default function OrderSuccessPage() {
|
|||||||
{order.status === 'PENDING_PAYMENT' ? (
|
{order.status === 'PENDING_PAYMENT' ? (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
{/* Payment Method Selector */}
|
{/* Payment Method Selector */}
|
||||||
<div className="bg-white p-4 rounded-2xl border border-spa-stone/10 shadow-sm flex flex-col md:flex-row gap-3">
|
<div className="bg-spa-light p-4 rounded-2xl border border-spa-stone/10 shadow-sm flex flex-col md:flex-row gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => setPaymentMethod('bank')}
|
onClick={() => setPaymentMethod('bank')}
|
||||||
className={`flex-1 py-4 px-6 rounded-xl font-bold flex items-center justify-center gap-3 border transition-all ${
|
className={`flex-1 py-4 px-6 rounded-xl font-bold flex items-center justify-center gap-3 border transition-all ${
|
||||||
paymentMethod === 'bank'
|
paymentMethod === 'bank'
|
||||||
? 'bg-spa-forest text-white border-spa-forest shadow-sm'
|
? 'bg-spa-forest text-spa-light border-spa-forest shadow-sm'
|
||||||
: 'bg-white text-spa-stone hover:bg-spa-light border-spa-stone/10'
|
: 'bg-spa-light text-spa-stone hover:bg-spa-light border-spa-stone/10'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Landmark className="w-5 h-5" />
|
<Landmark className="w-5 h-5" />
|
||||||
@@ -309,8 +309,8 @@ export default function OrderSuccessPage() {
|
|||||||
onClick={() => setPaymentMethod('promptpay')}
|
onClick={() => setPaymentMethod('promptpay')}
|
||||||
className={`flex-1 py-4 px-6 rounded-xl font-bold flex items-center justify-center gap-3 border transition-all ${
|
className={`flex-1 py-4 px-6 rounded-xl font-bold flex items-center justify-center gap-3 border transition-all ${
|
||||||
paymentMethod === 'promptpay'
|
paymentMethod === 'promptpay'
|
||||||
? 'bg-spa-forest text-white border-spa-forest shadow-sm'
|
? 'bg-spa-forest text-spa-light border-spa-forest shadow-sm'
|
||||||
: 'bg-white text-spa-stone hover:bg-spa-light border-spa-stone/10'
|
: 'bg-spa-light text-spa-stone hover:bg-spa-light border-spa-stone/10'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<QrCode className="w-5 h-5" />
|
<QrCode className="w-5 h-5" />
|
||||||
@@ -320,8 +320,8 @@ export default function OrderSuccessPage() {
|
|||||||
onClick={() => setPaymentMethod('card')}
|
onClick={() => setPaymentMethod('card')}
|
||||||
className={`flex-1 py-4 px-6 rounded-xl font-bold flex items-center justify-center gap-3 border transition-all ${
|
className={`flex-1 py-4 px-6 rounded-xl font-bold flex items-center justify-center gap-3 border transition-all ${
|
||||||
paymentMethod === 'card'
|
paymentMethod === 'card'
|
||||||
? 'bg-spa-forest text-white border-spa-forest shadow-sm'
|
? 'bg-spa-forest text-spa-light border-spa-forest shadow-sm'
|
||||||
: 'bg-white text-spa-stone hover:bg-spa-light border-spa-stone/10'
|
: 'bg-spa-light text-spa-stone hover:bg-spa-light border-spa-stone/10'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<CreditCard className="w-5 h-5" />
|
<CreditCard className="w-5 h-5" />
|
||||||
@@ -332,7 +332,7 @@ export default function OrderSuccessPage() {
|
|||||||
{/* Bank Transfer Layout */}
|
{/* Bank Transfer Layout */}
|
||||||
{paymentMethod === 'bank' && settings && (
|
{paymentMethod === 'bank' && settings && (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 text-center space-y-6 relative overflow-hidden shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 text-center space-y-6 relative overflow-hidden shadow-sm">
|
||||||
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-spa-forest to-transparent opacity-50" />
|
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-spa-forest to-transparent opacity-50" />
|
||||||
<h2 className="text-xs uppercase font-bold tracking-widest text-spa-stone/80">Payment Required</h2>
|
<h2 className="text-xs uppercase font-bold tracking-widest text-spa-stone/80">Payment Required</h2>
|
||||||
<div className="text-3xl font-bold text-spa-forest">{formatPrice(order.totalAmount)}</div>
|
<div className="text-3xl font-bold text-spa-forest">{formatPrice(order.totalAmount)}</div>
|
||||||
@@ -348,7 +348,7 @@ export default function OrderSuccessPage() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={copyToClipboard}
|
onClick={copyToClipboard}
|
||||||
className="p-2 bg-white rounded-lg hover:bg-spa-stone/10 border border-spa-stone/20 transition-colors shrink-0 shadow-sm"
|
className="p-2 bg-spa-light rounded-lg hover:bg-spa-stone/10 border border-spa-stone/20 transition-colors shrink-0 shadow-sm"
|
||||||
title="Copy account number"
|
title="Copy account number"
|
||||||
>
|
>
|
||||||
{copied ? <CheckCircle2 className="w-5 h-5 text-emerald-500" /> : <Copy className="w-5 h-5 text-spa-stone/80" />}
|
{copied ? <CheckCircle2 className="w-5 h-5 text-emerald-500" /> : <Copy className="w-5 h-5 text-spa-stone/80" />}
|
||||||
@@ -362,7 +362,7 @@ export default function OrderSuccessPage() {
|
|||||||
<p className="text-xs text-spa-stone font-medium">Please transfer the exact amount and upload your slip below.</p>
|
<p className="text-xs text-spa-stone font-medium">Please transfer the exact amount and upload your slip below.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 space-y-6 shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 space-y-6 shadow-sm">
|
||||||
<h2 className="text-sm font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-4">Payment Slip Verification</h2>
|
<h2 className="text-sm font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-4">Payment Slip Verification</h2>
|
||||||
{order.paymentSlipUrl ? (
|
{order.paymentSlipUrl ? (
|
||||||
<div className="flex flex-col items-center justify-center space-y-4 py-4">
|
<div className="flex flex-col items-center justify-center space-y-4 py-4">
|
||||||
@@ -404,7 +404,7 @@ export default function OrderSuccessPage() {
|
|||||||
|
|
||||||
{/* PromptPay Layout */}
|
{/* PromptPay Layout */}
|
||||||
{paymentMethod === 'promptpay' && (
|
{paymentMethod === 'promptpay' && (
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 text-center space-y-6 relative overflow-hidden shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 text-center space-y-6 relative overflow-hidden shadow-sm">
|
||||||
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-spa-forest to-transparent opacity-50" />
|
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-spa-forest to-transparent opacity-50" />
|
||||||
<h2 className="text-xs uppercase font-bold tracking-widest text-spa-stone/80">PromptPay QR Code</h2>
|
<h2 className="text-xs uppercase font-bold tracking-widest text-spa-stone/80">PromptPay QR Code</h2>
|
||||||
<div className="text-3xl font-bold text-spa-forest">{formatPrice(order.totalAmount)}</div>
|
<div className="text-3xl font-bold text-spa-forest">{formatPrice(order.totalAmount)}</div>
|
||||||
@@ -414,7 +414,7 @@ export default function OrderSuccessPage() {
|
|||||||
<button
|
<button
|
||||||
onClick={handleGeneratePromptPay}
|
onClick={handleGeneratePromptPay}
|
||||||
disabled={generatingQr}
|
disabled={generatingQr}
|
||||||
className="px-8 py-4 bg-spa-forest text-white font-bold rounded-xl tracking-widest uppercase hover:bg-spa-stone transition-all duration-300 disabled:opacity-50 flex items-center justify-center gap-3 mx-auto"
|
className="px-8 py-4 bg-spa-forest text-spa-light font-bold rounded-xl tracking-widest uppercase hover:bg-spa-stone transition-all duration-300 disabled:opacity-50 flex items-center justify-center gap-3 mx-auto"
|
||||||
>
|
>
|
||||||
{generatingQr ? <Loader2 className="w-5 h-5 animate-spin" /> : <QrCode className="w-5 h-5" />}
|
{generatingQr ? <Loader2 className="w-5 h-5 animate-spin" /> : <QrCode className="w-5 h-5" />}
|
||||||
<span>สร้างคิวอาร์โค้ด PromptPay</span>
|
<span>สร้างคิวอาร์โค้ด PromptPay</span>
|
||||||
@@ -422,7 +422,7 @@ export default function OrderSuccessPage() {
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="space-y-6 max-w-sm mx-auto">
|
<div className="space-y-6 max-w-sm mx-auto">
|
||||||
<div className="bg-white p-4 rounded-xl border border-spa-stone/20 inline-block shadow-sm">
|
<div className="bg-spa-light p-4 rounded-xl border border-spa-stone/20 inline-block shadow-sm">
|
||||||
<img src={qrCodeUrl} alt="PromptPay QR Code" className="w-64 h-64 object-contain mx-auto" />
|
<img src={qrCodeUrl} alt="PromptPay QR Code" className="w-64 h-64 object-contain mx-auto" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -452,7 +452,7 @@ export default function OrderSuccessPage() {
|
|||||||
|
|
||||||
{/* Credit Card Layout */}
|
{/* Credit Card Layout */}
|
||||||
{paymentMethod === 'card' && (
|
{paymentMethod === 'card' && (
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 space-y-6 relative overflow-hidden shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 space-y-6 relative overflow-hidden shadow-sm">
|
||||||
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-spa-forest to-transparent opacity-50" />
|
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-transparent via-spa-forest to-transparent opacity-50" />
|
||||||
<div className="text-center space-y-2">
|
<div className="text-center space-y-2">
|
||||||
<h2 className="text-xs uppercase font-bold tracking-widest text-spa-stone/80">Credit / Debit Card</h2>
|
<h2 className="text-xs uppercase font-bold tracking-widest text-spa-stone/80">Credit / Debit Card</h2>
|
||||||
@@ -527,7 +527,7 @@ export default function OrderSuccessPage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={payingCard}
|
disabled={payingCard}
|
||||||
className="w-full py-4 bg-spa-forest text-white font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone transition-all duration-300 disabled:opacity-50 flex items-center justify-center gap-2 mt-4"
|
className="w-full py-4 bg-spa-forest text-spa-light font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone transition-all duration-300 disabled:opacity-50 flex items-center justify-center gap-2 mt-4"
|
||||||
>
|
>
|
||||||
{payingCard ? <Loader2 className="w-5 h-5 animate-spin" /> : <CreditCard className="w-5 h-5" />}
|
{payingCard ? <Loader2 className="w-5 h-5 animate-spin" /> : <CreditCard className="w-5 h-5" />}
|
||||||
<span>ชำระเงิน {formatPrice(order.totalAmount)}</span>
|
<span>ชำระเงิน {formatPrice(order.totalAmount)}</span>
|
||||||
@@ -537,7 +537,7 @@ export default function OrderSuccessPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 text-center space-y-6 shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 text-center space-y-6 shadow-sm">
|
||||||
<div className="w-16 h-16 bg-emerald-500/10 rounded-full flex items-center justify-center border border-emerald-500/20 mx-auto">
|
<div className="w-16 h-16 bg-emerald-500/10 rounded-full flex items-center justify-center border border-emerald-500/20 mx-auto">
|
||||||
<CheckCircle2 className="w-8 h-8 text-emerald-500" />
|
<CheckCircle2 className="w-8 h-8 text-emerald-500" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -220,12 +220,12 @@ export default function ProductDetailPage() {
|
|||||||
onClick={() => setSelectedVariant(i)}
|
onClick={() => setSelectedVariant(i)}
|
||||||
className={`px-5 py-2.5 rounded-lg text-sm font-bold uppercase tracking-widest transition-all duration-300 border ${
|
className={`px-5 py-2.5 rounded-lg text-sm font-bold uppercase tracking-widest transition-all duration-300 border ${
|
||||||
selectedVariant === i
|
selectedVariant === i
|
||||||
? 'bg-spa-forest text-white border-spa-forest shadow-md'
|
? 'bg-spa-forest text-spa-light border-spa-forest shadow-md'
|
||||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
: 'bg-spa-light text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{v.size}
|
{v.size}
|
||||||
<span className={`block text-[10px] mt-0.5 ${selectedVariant === i ? 'text-white/80' : 'text-spa-stone/60'}`}>
|
<span className={`block text-[10px] mt-0.5 ${selectedVariant === i ? 'text-spa-light' : 'text-spa-stone/60'}`}>
|
||||||
{formatPrice(v.price)}
|
{formatPrice(v.price)}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -237,11 +237,11 @@ export default function ProductDetailPage() {
|
|||||||
<div className="hidden md:flex gap-4">
|
<div className="hidden md:flex gap-4">
|
||||||
<button
|
<button
|
||||||
onClick={handleAddToCart}
|
onClick={handleAddToCart}
|
||||||
className="flex-1 py-4 bg-spa-forest text-white font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300"
|
className="flex-1 py-4 bg-spa-forest text-spa-light font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300"
|
||||||
>
|
>
|
||||||
{t('cart.addToCart')}
|
{t('cart.addToCart')}
|
||||||
</button>
|
</button>
|
||||||
<button className="bg-white border border-spa-stone/20 text-spa-stone p-4 rounded-lg hover:border-spa-forest hover:text-spa-forest shadow-sm transition-all">
|
<button className="bg-spa-light border border-spa-stone/20 text-spa-stone p-4 rounded-lg hover:border-spa-forest hover:text-spa-forest shadow-sm transition-all">
|
||||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -322,7 +322,7 @@ export default function ProductDetailPage() {
|
|||||||
<Footer categories={categories} />
|
<Footer categories={categories} />
|
||||||
|
|
||||||
{/* Mobile Sticky Add to Cart Bar */}
|
{/* Mobile Sticky Add to Cart Bar */}
|
||||||
<div className="md:hidden fixed bottom-0 left-0 right-0 p-4 bg-white/95 backdrop-blur-xl border-t border-spa-stone/10 z-40 animate-fade-in flex flex-col gap-3 shadow-[0_-10px_30px_rgba(0,0,0,0.05)]">
|
<div className="md:hidden fixed bottom-0 left-0 right-0 p-4 bg-spa-light backdrop-blur-xl border-t border-spa-stone/10 z-40 animate-fade-in flex flex-col gap-3 shadow-[0_-10px_30px_rgba(0,0,0,0.05)]">
|
||||||
|
|
||||||
{/* Mobile Variant Selector */}
|
{/* Mobile Variant Selector */}
|
||||||
{product.variants && product.variants.length > 1 && (
|
{product.variants && product.variants.length > 1 && (
|
||||||
@@ -333,8 +333,8 @@ export default function ProductDetailPage() {
|
|||||||
onClick={() => setSelectedVariant(i)}
|
onClick={() => setSelectedVariant(i)}
|
||||||
className={`flex-shrink-0 px-4 py-2 rounded-lg text-[10px] font-bold uppercase tracking-widest transition-all border whitespace-nowrap ${
|
className={`flex-shrink-0 px-4 py-2 rounded-lg text-[10px] font-bold uppercase tracking-widest transition-all border whitespace-nowrap ${
|
||||||
selectedVariant === i
|
selectedVariant === i
|
||||||
? 'bg-spa-forest border-spa-forest text-white'
|
? 'bg-spa-forest border-spa-forest text-spa-light'
|
||||||
: 'bg-white border-spa-stone/20 text-spa-stone'
|
: 'bg-spa-light border-spa-stone/20 text-spa-stone'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{v.size}
|
{v.size}
|
||||||
@@ -354,7 +354,7 @@ export default function ProductDetailPage() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddToCart}
|
onClick={handleAddToCart}
|
||||||
className="flex-1 py-3.5 bg-spa-forest text-white font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone shadow-sm transition-all flex justify-center items-center gap-2"
|
className="flex-1 py-3.5 bg-spa-forest text-spa-light font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone shadow-sm transition-all flex justify-center items-center gap-2"
|
||||||
>
|
>
|
||||||
{t('cart.addToCart')}
|
{t('cart.addToCart')}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ export default function ProfilePage() {
|
|||||||
|
|
||||||
{!customer ? (
|
{!customer ? (
|
||||||
/* Login CTA screen */
|
/* Login CTA screen */
|
||||||
<div className="max-w-md mx-auto text-center space-y-8 py-20 animate-fade-in bg-white p-10 rounded-3xl border border-spa-stone/10 shadow-sm mt-10">
|
<div className="max-w-md mx-auto text-center space-y-8 py-20 animate-fade-in bg-spa-light p-10 rounded-3xl border border-spa-stone/10 shadow-sm mt-10">
|
||||||
<div className="w-20 h-20 bg-spa-light rounded-full flex items-center justify-center mx-auto border border-spa-stone/10 shadow-inner">
|
<div className="w-20 h-20 bg-spa-light rounded-full flex items-center justify-center mx-auto border border-spa-stone/10 shadow-inner">
|
||||||
<User className="w-10 h-10 text-spa-stone" />
|
<User className="w-10 h-10 text-spa-stone" />
|
||||||
</div>
|
</div>
|
||||||
@@ -320,7 +320,7 @@ export default function ProfilePage() {
|
|||||||
alert('LINE LIFF System is initializing. Please refresh.')
|
alert('LINE LIFF System is initializing. Please refresh.')
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
className="w-full py-4 bg-spa-forest hover:bg-spa-stone text-white font-bold rounded-xl tracking-widest uppercase transition-all duration-300 shadow-sm flex items-center justify-center gap-3"
|
className="w-full py-4 bg-spa-forest hover:bg-spa-stone text-spa-light font-bold rounded-xl tracking-widest uppercase transition-all duration-300 shadow-sm flex items-center justify-center gap-3"
|
||||||
>
|
>
|
||||||
<Award className="w-5 h-5" />
|
<Award className="w-5 h-5" />
|
||||||
<span>เข้าสู่ระบบผ่าน LINE</span>
|
<span>เข้าสู่ระบบผ่าน LINE</span>
|
||||||
@@ -331,7 +331,7 @@ export default function ProfilePage() {
|
|||||||
/* Logged In Dashboard Layout */
|
/* Logged In Dashboard Layout */
|
||||||
<div className="space-y-10 animate-fade-in">
|
<div className="space-y-10 animate-fade-in">
|
||||||
{/* Header Profile Banner */}
|
{/* Header Profile Banner */}
|
||||||
<div className="bg-white rounded-2xl border border-spa-stone/10 p-5 sm:p-6 flex flex-col sm:flex-row justify-between items-center sm:items-center gap-4 shadow-sm relative overflow-hidden">
|
<div className="bg-spa-light rounded-2xl border border-spa-stone/10 p-5 sm:p-6 flex flex-col sm:flex-row justify-between items-center sm:items-center gap-4 shadow-sm relative overflow-hidden">
|
||||||
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-spa-sage via-spa-forest to-spa-stone opacity-40" />
|
<div className="absolute top-0 inset-x-0 h-1 bg-gradient-to-r from-spa-sage via-spa-forest to-spa-stone opacity-40" />
|
||||||
|
|
||||||
{/* User Avatar & Info */}
|
{/* User Avatar & Info */}
|
||||||
@@ -459,7 +459,7 @@ export default function ProfilePage() {
|
|||||||
<Loader2 className="w-8 h-8 text-spa-forest animate-spin" />
|
<Loader2 className="w-8 h-8 text-spa-forest animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
) : orders.length === 0 ? (
|
) : orders.length === 0 ? (
|
||||||
<div className="text-center py-16 bg-white rounded-2xl border border-spa-stone/10 space-y-4 shadow-sm">
|
<div className="text-center py-16 bg-spa-light rounded-2xl border border-spa-stone/10 space-y-4 shadow-sm">
|
||||||
<Package className="w-12 h-12 text-spa-stone/40 mx-auto" />
|
<Package className="w-12 h-12 text-spa-stone/40 mx-auto" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-pop-black">ไม่มีประวัติการสั่งซื้อ</p>
|
<p className="font-bold text-pop-black">ไม่มีประวัติการสั่งซื้อ</p>
|
||||||
@@ -467,7 +467,7 @@ export default function ProfilePage() {
|
|||||||
</div>
|
</div>
|
||||||
<Link
|
<Link
|
||||||
to="/shop"
|
to="/shop"
|
||||||
className="inline-block px-6 py-2.5 bg-spa-forest hover:bg-spa-stone text-white font-bold rounded-lg text-xs uppercase tracking-wider transition-colors shadow-sm"
|
className="inline-block px-6 py-2.5 bg-spa-forest hover:bg-spa-stone text-spa-light font-bold rounded-lg text-xs uppercase tracking-wider transition-colors shadow-sm"
|
||||||
>
|
>
|
||||||
ไปหน้าช้อปปิ้ง
|
ไปหน้าช้อปปิ้ง
|
||||||
</Link>
|
</Link>
|
||||||
@@ -477,7 +477,7 @@ export default function ProfilePage() {
|
|||||||
{orders.map(order => (
|
{orders.map(order => (
|
||||||
<div
|
<div
|
||||||
key={order.id}
|
key={order.id}
|
||||||
className="bg-white border border-spa-stone/10 rounded-2xl p-6 shadow-sm hover:border-spa-stone transition-all duration-300 space-y-4"
|
className="bg-spa-light border border-spa-stone/10 rounded-2xl p-6 shadow-sm hover:border-spa-stone transition-all duration-300 space-y-4"
|
||||||
>
|
>
|
||||||
{/* Card Header */}
|
{/* Card Header */}
|
||||||
<div className="flex flex-col md:flex-row justify-between md:items-center gap-3 border-b border-spa-stone/10 pb-4">
|
<div className="flex flex-col md:flex-row justify-between md:items-center gap-3 border-b border-spa-stone/10 pb-4">
|
||||||
@@ -550,7 +550,7 @@ export default function ProfilePage() {
|
|||||||
resetAddressForm()
|
resetAddressForm()
|
||||||
setShowAddressForm(true)
|
setShowAddressForm(true)
|
||||||
}}
|
}}
|
||||||
className="px-4 py-2 bg-spa-forest hover:bg-spa-stone text-white font-bold rounded-lg text-xs uppercase tracking-wider transition-colors shadow-sm flex items-center gap-2"
|
className="px-4 py-2 bg-spa-forest hover:bg-spa-stone text-spa-light font-bold rounded-lg text-xs uppercase tracking-wider transition-colors shadow-sm flex items-center gap-2"
|
||||||
>
|
>
|
||||||
<Plus className="w-3.5 h-3.5" />
|
<Plus className="w-3.5 h-3.5" />
|
||||||
<span>เพิ่มที่อยู่ใหม่</span>
|
<span>เพิ่มที่อยู่ใหม่</span>
|
||||||
@@ -560,7 +560,7 @@ export default function ProfilePage() {
|
|||||||
|
|
||||||
{/* Address Creation/Editing Form */}
|
{/* Address Creation/Editing Form */}
|
||||||
{showAddressForm && (
|
{showAddressForm && (
|
||||||
<form onSubmit={handleAddressSubmit} className="bg-white border border-spa-stone/15 p-6 rounded-2xl shadow-sm space-y-5 animate-slide-down">
|
<form onSubmit={handleAddressSubmit} className="bg-spa-light border border-spa-stone/15 p-6 rounded-2xl shadow-sm space-y-5 animate-slide-down">
|
||||||
<h3 className="text-sm font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-2">
|
<h3 className="text-sm font-bold uppercase tracking-widest text-spa-forest border-b border-spa-stone/10 pb-2">
|
||||||
{editingAddress ? 'แก้ไขที่อยู่จัดส่ง' : 'เพิ่มที่อยู่จัดส่งใหม่'}
|
{editingAddress ? 'แก้ไขที่อยู่จัดส่ง' : 'เพิ่มที่อยู่จัดส่งใหม่'}
|
||||||
</h3>
|
</h3>
|
||||||
@@ -636,7 +636,7 @@ export default function ProfilePage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submittingAddress}
|
disabled={submittingAddress}
|
||||||
className="px-5 py-2.5 bg-spa-forest hover:bg-spa-stone text-white font-bold rounded-lg text-xs uppercase tracking-wider transition-colors disabled:opacity-50 flex items-center gap-2"
|
className="px-5 py-2.5 bg-spa-forest hover:bg-spa-stone text-spa-light font-bold rounded-lg text-xs uppercase tracking-wider transition-colors disabled:opacity-50 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
{submittingAddress && <Loader2 className="w-3.5 h-3.5 animate-spin" />}
|
{submittingAddress && <Loader2 className="w-3.5 h-3.5 animate-spin" />}
|
||||||
<span>บันทึกที่อยู่</span>
|
<span>บันทึกที่อยู่</span>
|
||||||
@@ -650,7 +650,7 @@ export default function ProfilePage() {
|
|||||||
<Loader2 className="w-8 h-8 text-spa-forest animate-spin" />
|
<Loader2 className="w-8 h-8 text-spa-forest animate-spin" />
|
||||||
</div>
|
</div>
|
||||||
) : addresses.length === 0 ? (
|
) : addresses.length === 0 ? (
|
||||||
<div className="text-center py-16 bg-white rounded-2xl border border-spa-stone/10 space-y-4 shadow-sm">
|
<div className="text-center py-16 bg-spa-light rounded-2xl border border-spa-stone/10 space-y-4 shadow-sm">
|
||||||
<MapPin className="w-12 h-12 text-spa-stone/40 mx-auto" />
|
<MapPin className="w-12 h-12 text-spa-stone/40 mx-auto" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-pop-black">ไม่มีข้อมูลที่อยู่จัดส่ง</p>
|
<p className="font-bold text-pop-black">ไม่มีข้อมูลที่อยู่จัดส่ง</p>
|
||||||
@@ -662,7 +662,7 @@ export default function ProfilePage() {
|
|||||||
{addresses.map(addr => (
|
{addresses.map(addr => (
|
||||||
<div
|
<div
|
||||||
key={addr.id}
|
key={addr.id}
|
||||||
className={`bg-white border rounded-2xl p-5 shadow-sm space-y-4 transition-all duration-300 relative ${
|
className={`bg-spa-light border rounded-2xl p-5 shadow-sm space-y-4 transition-all duration-300 relative ${
|
||||||
addr.isDefault
|
addr.isDefault
|
||||||
? 'border-spa-forest ring-1 ring-spa-forest/20'
|
? 'border-spa-forest ring-1 ring-spa-forest/20'
|
||||||
: 'border-spa-stone/10 hover:border-spa-stone'
|
: 'border-spa-stone/10 hover:border-spa-stone'
|
||||||
@@ -729,7 +729,7 @@ export default function ProfilePage() {
|
|||||||
<span>ข้อมูลติดต่อสมาชิก</span>
|
<span>ข้อมูลติดต่อสมาชิก</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<form onSubmit={handleSettingsSubmit} className="bg-white border border-spa-stone/10 rounded-2xl p-6 shadow-sm max-w-lg space-y-5">
|
<form onSubmit={handleSettingsSubmit} className="bg-spa-light border border-spa-stone/10 rounded-2xl p-6 shadow-sm max-w-lg space-y-5">
|
||||||
{settingsSuccess && (
|
{settingsSuccess && (
|
||||||
<div className="p-4 bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 font-bold rounded-xl text-xs text-center flex items-center justify-center gap-2">
|
<div className="p-4 bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 font-bold rounded-xl text-xs text-center flex items-center justify-center gap-2">
|
||||||
<Check className="w-4 h-4" />
|
<Check className="w-4 h-4" />
|
||||||
@@ -763,7 +763,7 @@ export default function ProfilePage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={submittingSettings}
|
disabled={submittingSettings}
|
||||||
className="w-full py-3 bg-spa-forest hover:bg-spa-stone text-white font-bold rounded-lg text-xs uppercase tracking-widest transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
|
className="w-full py-3 bg-spa-forest hover:bg-spa-stone text-spa-light font-bold rounded-lg text-xs uppercase tracking-widest transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
{submittingSettings ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
|
{submittingSettings ? <Loader2 className="w-4 h-4 animate-spin" /> : <Check className="w-4 h-4" />}
|
||||||
<span>บันทึกข้อมูลติดต่อ</span>
|
<span>บันทึกข้อมูลติดต่อ</span>
|
||||||
|
|||||||
@@ -59,10 +59,10 @@ export default function ScentGuidePage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="pt-32 pb-16 px-6 text-center space-y-4 border-b border-spa-stone/10 bg-white">
|
<div className="pt-32 pb-16 px-6 text-center space-y-4 border-b border-spa-stone/10 bg-spa-light">
|
||||||
<p className="text-spa-forest uppercase tracking-[0.4em] text-xs font-semibold opacity-70">Journal</p>
|
<p className="text-spa-forest uppercase tracking-[0.4em] text-xs font-semibold opacity-70">Journal</p>
|
||||||
<h1 className="text-5xl font-bold">Scent Guide</h1>
|
<h1 className="text-5xl font-bold">Scent Guide</h1>
|
||||||
<p className="text-slate-400 max-w-lg mx-auto">
|
<p className="text-spa-stone max-w-lg mx-auto">
|
||||||
Explore our collection of articles, guides, and sensory knowledge.
|
Explore our collection of articles, guides, and sensory knowledge.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,7 +70,7 @@ export default function ScentGuidePage() {
|
|||||||
|
|
||||||
<div className="flex-1 max-w-7xl mx-auto px-6 py-16 w-full">
|
<div className="flex-1 max-w-7xl mx-auto px-6 py-16 w-full">
|
||||||
{articles.length === 0 ? (
|
{articles.length === 0 ? (
|
||||||
<div className="text-center text-slate-500 italic py-20">
|
<div className="text-center text-spa-stone italic py-20">
|
||||||
Articles coming soon. Check back later!
|
Articles coming soon. Check back later!
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -81,12 +81,12 @@ export default function ScentGuidePage() {
|
|||||||
to={`/article/${article.slug}`}
|
to={`/article/${article.slug}`}
|
||||||
className="group block bg-pop-bg rounded-2xl overflow-hidden border border-spa-stone/10 hover:border-spa-forest transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl flex flex-col"
|
className="group block bg-pop-bg rounded-2xl overflow-hidden border border-spa-stone/10 hover:border-spa-forest transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl flex flex-col"
|
||||||
>
|
>
|
||||||
<div className="aspect-[4/3] bg-white overflow-hidden shrink-0">
|
<div className="aspect-[4/3] bg-spa-light overflow-hidden shrink-0">
|
||||||
{article.coverImage ? (
|
{article.coverImage ? (
|
||||||
<img src={article.coverImage} alt={article.title} className="w-full h-full object-cover opacity-80 group-hover:opacity-100 group-hover:scale-105 transition-all duration-500" />
|
<img src={article.coverImage} alt={article.title} className="w-full h-full object-cover opacity-80 group-hover:opacity-100 group-hover:scale-105 transition-all duration-500" />
|
||||||
) : (
|
) : (
|
||||||
<div className="w-full h-full bg-white/5 flex items-center justify-center">
|
<div className="w-full h-full bg-spa-light flex items-center justify-center">
|
||||||
<span className="text-slate-600 text-sm tracking-widest uppercase">No Image</span>
|
<span className="text-spa-stone text-sm tracking-widest uppercase">No Image</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -96,7 +96,7 @@ export default function ScentGuidePage() {
|
|||||||
</p>
|
</p>
|
||||||
<h3 className="text-xl font-bold leading-tight group-hover:text-spa-forest transition-colors">{article.title}</h3>
|
<h3 className="text-xl font-bold leading-tight group-hover:text-spa-forest transition-colors">{article.title}</h3>
|
||||||
{/* Remove HTML tags for purely excerpt preview */}
|
{/* Remove HTML tags for purely excerpt preview */}
|
||||||
<div className="text-slate-400 text-sm line-clamp-3" dangerouslySetInnerHTML={{ __html: article.content }} />
|
<div className="text-spa-stone text-sm line-clamp-3" dangerouslySetInnerHTML={{ __html: article.content }} />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export default function SearchPage() {
|
|||||||
<h1 className="text-5xl font-bold text-pop-black">Search Products</h1>
|
<h1 className="text-5xl font-bold text-pop-black">Search Products</h1>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit} className="relative">
|
<form onSubmit={handleSubmit} className="relative">
|
||||||
<div className="flex bg-white border border-spa-stone/20 rounded-2xl overflow-hidden shadow-sm focus-within:border-spa-forest focus-within:ring-1 focus-within:ring-spa-forest transition-all">
|
<div className="flex bg-spa-light border border-spa-stone/20 rounded-2xl overflow-hidden shadow-sm focus-within:border-spa-forest focus-within:ring-1 focus-within:ring-spa-forest transition-all">
|
||||||
<svg className="w-5 h-5 text-spa-stone m-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-5 h-5 text-spa-stone m-4 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -114,7 +114,7 @@ export default function SearchPage() {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-spa-forest text-white px-8 font-bold text-xs uppercase tracking-widest hover:bg-spa-stone transition-colors shrink-0"
|
className="bg-spa-forest text-spa-light px-8 font-bold text-xs uppercase tracking-widest hover:bg-spa-stone transition-colors shrink-0"
|
||||||
>
|
>
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
@@ -127,7 +127,7 @@ export default function SearchPage() {
|
|||||||
<Link
|
<Link
|
||||||
key={cat.id}
|
key={cat.id}
|
||||||
to={`/search?q=${encodeURIComponent(typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[i18n.language] || (cat.name as any)?.en || '')}`}
|
to={`/search?q=${encodeURIComponent(typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[i18n.language] || (cat.name as any)?.en || '')}`}
|
||||||
className="bg-white border border-spa-stone/20 px-4 py-1.5 rounded-full text-xs font-medium text-spa-stone hover:border-spa-forest hover:text-spa-forest shadow-sm transition-all duration-300"
|
className="bg-spa-light border border-spa-stone/20 px-4 py-1.5 rounded-full text-xs font-medium text-spa-stone hover:border-spa-forest hover:text-spa-forest shadow-sm transition-all duration-300"
|
||||||
>
|
>
|
||||||
{typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[i18n.language] || (cat.name as any)?.en || ''}
|
{typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[i18n.language] || (cat.name as any)?.en || ''}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export default function ShopPage() {
|
|||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
<div className="max-w-md mx-auto mt-6">
|
<div className="max-w-md mx-auto mt-6">
|
||||||
<div className="flex bg-white border border-spa-stone/20 rounded-xl overflow-hidden shadow-sm focus-within:border-spa-forest focus-within:ring-1 focus-within:ring-spa-forest transition-all">
|
<div className="flex bg-spa-light border border-spa-stone/20 rounded-xl overflow-hidden shadow-sm focus-within:border-spa-forest focus-within:ring-1 focus-within:ring-spa-forest transition-all">
|
||||||
<svg className="w-4 h-4 text-spa-stone m-3.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-4 h-4 text-spa-stone m-3.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -173,7 +173,7 @@ export default function ShopPage() {
|
|||||||
{/* Render Search bar below Hero block if Hero is configured */}
|
{/* Render Search bar below Hero block if Hero is configured */}
|
||||||
{heroSections.length > 0 && (
|
{heroSections.length > 0 && (
|
||||||
<div className="max-w-md mx-auto mt-8 px-6">
|
<div className="max-w-md mx-auto mt-8 px-6">
|
||||||
<div className="flex bg-white border border-spa-stone/20 rounded-xl overflow-hidden shadow-sm focus-within:border-spa-forest focus-within:ring-1 focus-within:ring-spa-forest transition-all">
|
<div className="flex bg-spa-light border border-spa-stone/20 rounded-xl overflow-hidden shadow-sm focus-within:border-spa-forest focus-within:ring-1 focus-within:ring-spa-forest transition-all">
|
||||||
<svg className="w-4 h-4 text-spa-stone m-3.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg className="w-4 h-4 text-spa-stone m-3.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
@@ -195,8 +195,8 @@ export default function ShopPage() {
|
|||||||
onClick={() => setActiveCategory(null)}
|
onClick={() => setActiveCategory(null)}
|
||||||
className={`px-5 py-2 rounded-full text-xs font-bold uppercase tracking-widest transition-all duration-300 border ${
|
className={`px-5 py-2 rounded-full text-xs font-bold uppercase tracking-widest transition-all duration-300 border ${
|
||||||
activeCategory === null
|
activeCategory === null
|
||||||
? 'bg-spa-forest text-white border-spa-forest shadow-md'
|
? 'bg-spa-forest text-spa-light border-spa-forest shadow-md'
|
||||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
: 'bg-spa-light text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
All
|
All
|
||||||
@@ -207,8 +207,8 @@ export default function ShopPage() {
|
|||||||
onClick={() => setActiveCategory(cat.id)}
|
onClick={() => setActiveCategory(cat.id)}
|
||||||
className={`px-5 py-2 rounded-full text-xs font-bold uppercase tracking-widest transition-all duration-300 border ${
|
className={`px-5 py-2 rounded-full text-xs font-bold uppercase tracking-widest transition-all duration-300 border ${
|
||||||
activeCategory === cat.id
|
activeCategory === cat.id
|
||||||
? 'bg-spa-forest text-white border-spa-forest shadow-md'
|
? 'bg-spa-forest text-spa-light border-spa-forest shadow-md'
|
||||||
: 'bg-white text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
: 'bg-spa-light text-spa-stone border-spa-stone/20 hover:border-spa-forest hover:text-spa-forest shadow-sm'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[i18n.language] || (cat.name as any)?.en || ''}
|
{typeof cat.name === 'string' ? cat.name : (cat.name as any)?.[i18n.language] || (cat.name as any)?.en || ''}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export default function TrackOrderPage() {
|
|||||||
<div className="grid md:grid-cols-2 gap-12">
|
<div className="grid md:grid-cols-2 gap-12">
|
||||||
|
|
||||||
{/* Tracking Form */}
|
{/* Tracking Form */}
|
||||||
<div className="bg-white p-8 rounded-2xl border border-spa-stone/10 space-y-6 self-start shadow-sm">
|
<div className="bg-spa-light p-8 rounded-2xl border border-spa-stone/10 space-y-6 self-start shadow-sm">
|
||||||
<h2 className="text-lg font-bold uppercase tracking-widest border-b border-spa-stone/10 pb-4 flex items-center gap-2 text-spa-forest">
|
<h2 className="text-lg font-bold uppercase tracking-widest border-b border-spa-stone/10 pb-4 flex items-center gap-2 text-spa-forest">
|
||||||
<Search className="w-5 h-5" /> Manual Search
|
<Search className="w-5 h-5" /> Manual Search
|
||||||
</h2>
|
</h2>
|
||||||
@@ -183,7 +183,7 @@ export default function TrackOrderPage() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
className="w-full py-4 bg-spa-forest text-white font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300 disabled:opacity-50 flex items-center justify-center gap-2"
|
className="w-full py-4 bg-spa-forest text-spa-light font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone hover:shadow-lg transition-all duration-300 disabled:opacity-50 flex items-center justify-center gap-2"
|
||||||
>
|
>
|
||||||
{loading ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Find Order'}
|
{loading ? <Loader2 className="w-5 h-5 animate-spin" /> : 'Find Order'}
|
||||||
</button>
|
</button>
|
||||||
@@ -214,7 +214,7 @@ export default function TrackOrderPage() {
|
|||||||
<Link
|
<Link
|
||||||
key={order.id}
|
key={order.id}
|
||||||
to={`/order/${order.id}`}
|
to={`/order/${order.id}`}
|
||||||
className="flex justify-between items-center p-5 bg-white rounded-xl border border-spa-stone/10 shadow-sm hover:border-spa-forest transition-all group"
|
className="flex justify-between items-center p-5 bg-spa-light rounded-xl border border-spa-stone/10 shadow-sm hover:border-spa-forest transition-all group"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 mb-1">
|
<div className="flex items-center gap-2 mb-1">
|
||||||
@@ -235,7 +235,7 @@ export default function TrackOrderPage() {
|
|||||||
<p className="text-[10px] text-spa-stone uppercase">{order.items.length} items</p>
|
<p className="text-[10px] text-spa-stone uppercase">{order.items.length} items</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-10 h-10 rounded-full bg-spa-light flex items-center justify-center group-hover:bg-spa-forest transition-colors shrink-0">
|
<div className="w-10 h-10 rounded-full bg-spa-light flex items-center justify-center group-hover:bg-spa-forest transition-colors shrink-0">
|
||||||
<ChevronRight className="w-5 h-5 text-spa-stone/80 group-hover:text-white" />
|
<ChevronRight className="w-5 h-5 text-spa-stone/80 group-hover:text-spa-light" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -254,14 +254,14 @@ export default function TrackOrderPage() {
|
|||||||
<Link
|
<Link
|
||||||
key={id}
|
key={id}
|
||||||
to={`/order/${id}`}
|
to={`/order/${id}`}
|
||||||
className="flex justify-between items-center p-5 bg-white rounded-xl border border-spa-stone/10 shadow-sm hover:border-spa-forest transition-all group"
|
className="flex justify-between items-center p-5 bg-spa-light rounded-xl border border-spa-stone/10 shadow-sm hover:border-spa-forest transition-all group"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-spa-stone/80 uppercase tracking-widest mb-1 font-semibold">Order</p>
|
<p className="text-xs text-spa-stone/80 uppercase tracking-widest mb-1 font-semibold">Order</p>
|
||||||
<p className="text-xl font-bold font-mono text-spa-forest">#{id.toString().padStart(4, '0')}</p>
|
<p className="text-xl font-bold font-mono text-spa-forest">#{id.toString().padStart(4, '0')}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-10 h-10 rounded-full bg-spa-light flex items-center justify-center group-hover:bg-spa-forest transition-colors">
|
<div className="w-10 h-10 rounded-full bg-spa-light flex items-center justify-center group-hover:bg-spa-forest transition-colors">
|
||||||
<ChevronRight className="w-5 h-5 text-spa-stone/80 group-hover:text-white" />
|
<ChevronRight className="w-5 h-5 text-spa-stone/80 group-hover:text-spa-light" />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export default function WishlistPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{items.length === 0 ? (
|
{items.length === 0 ? (
|
||||||
<div className="text-center py-24 bg-white shadow-sm border border-spa-stone/10 rounded-3xl max-w-2xl mx-auto space-y-6">
|
<div className="text-center py-24 bg-spa-light shadow-sm border border-spa-stone/10 rounded-3xl max-w-2xl mx-auto space-y-6">
|
||||||
<div className="w-20 h-20 bg-spa-light rounded-full flex items-center justify-center mx-auto">
|
<div className="w-20 h-20 bg-spa-light rounded-full flex items-center justify-center mx-auto">
|
||||||
<Heart className="w-10 h-10 text-spa-stone/30" />
|
<Heart className="w-10 h-10 text-spa-stone/30" />
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +85,7 @@ export default function WishlistPage() {
|
|||||||
<div className="pt-4">
|
<div className="pt-4">
|
||||||
<Link
|
<Link
|
||||||
to="/shop"
|
to="/shop"
|
||||||
className="inline-block px-8 py-3.5 bg-spa-forest text-white font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone transition-all duration-300 shadow-sm"
|
className="inline-block px-8 py-3.5 bg-spa-forest text-spa-light font-bold rounded-lg tracking-widest uppercase hover:bg-spa-stone transition-all duration-300 shadow-sm"
|
||||||
>
|
>
|
||||||
Browse Shop
|
Browse Shop
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export default async function AboutPage({ params }: { params: Promise<{ lang: st
|
|||||||
<h2 className="text-4xl font-bold text-text-main">Ready to find your scent?</h2>
|
<h2 className="text-4xl font-bold text-text-main">Ready to find your scent?</h2>
|
||||||
<p className="mx-auto max-w-md text-muted-text">Let our Scent Guide help you discover the perfect blend for your moment.</p>
|
<p className="mx-auto max-w-md text-muted-text">Let our Scent Guide help you discover the perfect blend for your moment.</p>
|
||||||
<div className="flex flex-col justify-center gap-4 pt-4 sm:flex-row">
|
<div className="flex flex-col justify-center gap-4 pt-4 sm:flex-row">
|
||||||
<Link href="/scent-guide" className="rounded-full bg-text-main px-10 py-4 font-bold uppercase tracking-widest text-white transition hover:bg-brand-primary">
|
<Link href="/scent-guide" className="rounded-full bg-text-main px-10 py-4 font-bold uppercase tracking-widest text-surface-card transition hover:bg-brand-primary">
|
||||||
Take the Scent Guide
|
Take the Scent Guide
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/shop" className="rounded-full border border-brand-primary bg-surface-card px-10 py-4 font-bold uppercase tracking-widest text-brand-primary transition hover:bg-surface-page">
|
<Link href="/shop" className="rounded-full border border-brand-primary bg-surface-card px-10 py-4 font-bold uppercase tracking-widest text-brand-primary transition hover:bg-surface-page">
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export default async function CategoryPage({ params }: { params: Promise<{ id: s
|
|||||||
href={`/category/${item.id}`}
|
href={`/category/${item.id}`}
|
||||||
className={`rounded-full px-5 py-2 text-xs font-bold uppercase tracking-widest transition ${
|
className={`rounded-full px-5 py-2 text-xs font-bold uppercase tracking-widest transition ${
|
||||||
isActive
|
isActive
|
||||||
? 'bg-text-main text-white'
|
? 'bg-text-main text-surface-card'
|
||||||
: 'border border-line-primary/20 bg-surface-card text-muted-text hover:border-brand-primary hover:text-brand-primary'
|
: 'border border-line-primary/20 bg-surface-card text-muted-text hover:border-brand-primary hover:text-brand-primary'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export default async function CouponsPage({ params }: { params: Promise<{ lang:
|
|||||||
<p className="mb-6 text-muted-text">กรุณากลับมาตรวจสอบโปรโมชั่นใหม่ในภายหลัง</p>
|
<p className="mb-6 text-muted-text">กรุณากลับมาตรวจสอบโปรโมชั่นใหม่ในภายหลัง</p>
|
||||||
<Link
|
<Link
|
||||||
href="/shop"
|
href="/shop"
|
||||||
className="inline-flex rounded-full bg-brand-primary px-8 py-3 font-bold text-white transition hover:bg-brand-primary-hover"
|
className="inline-flex rounded-full bg-brand-primary px-8 py-3 font-bold text-surface-card transition hover:bg-brand-primary-hover"
|
||||||
>
|
>
|
||||||
ไปช้อปปิ้งกันก่อน
|
ไปช้อปปิ้งกันก่อน
|
||||||
</Link>
|
</Link>
|
||||||
@@ -116,7 +116,7 @@ export default async function CouponsPage({ params }: { params: Promise<{ lang:
|
|||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={`/shop?coupon=${encodeURIComponent(coupon.code)}`}
|
href={`/shop?coupon=${encodeURIComponent(coupon.code)}`}
|
||||||
className="flex w-full items-center justify-center gap-2 rounded-full bg-text-main py-4 font-bold text-white transition hover:bg-brand-primary"
|
className="flex w-full items-center justify-center gap-2 rounded-full bg-text-main py-4 font-bold text-surface-card transition hover:bg-brand-primary"
|
||||||
>
|
>
|
||||||
ช้อปพร้อมใช้คูปองเลย
|
ช้อปพร้อมใช้คูปองเลย
|
||||||
<ArrowRight className="h-4 w-4" />
|
<ArrowRight className="h-4 w-4" />
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export default function FaqPage() {
|
|||||||
onClick={() => setActiveCategory(category)}
|
onClick={() => setActiveCategory(category)}
|
||||||
className={`flex items-center gap-2 rounded-full px-5 py-2 text-xs font-bold uppercase tracking-widest transition-all duration-300 ${
|
className={`flex items-center gap-2 rounded-full px-5 py-2 text-xs font-bold uppercase tracking-widest transition-all duration-300 ${
|
||||||
activeCategory === category
|
activeCategory === category
|
||||||
? 'ui-tab-button-active bg-text-main text-white shadow-card'
|
? 'ui-tab-button-active bg-text-main text-surface-card shadow-card'
|
||||||
: 'ui-tab-button-inactive border border-line-primary/20 bg-surface-card hover:border-brand-primary hover:text-brand-primary'
|
: 'ui-tab-button-inactive border border-line-primary/20 bg-surface-card hover:border-brand-primary hover:text-brand-primary'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -119,7 +119,7 @@ export default function FaqPage() {
|
|||||||
<div className="text-3xl">💬</div>
|
<div className="text-3xl">💬</div>
|
||||||
<h3 className="text-xl font-bold text-text-main">Still have questions?</h3>
|
<h3 className="text-xl font-bold text-text-main">Still have questions?</h3>
|
||||||
<p className="text-sm text-muted-text">Our team is here to help — usually responds within 2 hours.</p>
|
<p className="text-sm text-muted-text">Our team is here to help — usually responds within 2 hours.</p>
|
||||||
<Link href="/contact" className="inline-block rounded-full bg-text-main px-8 py-3 text-xs font-bold uppercase tracking-widest text-white transition-colors duration-300 hover:bg-brand-primary">
|
<Link href="/contact" className="inline-block rounded-full bg-text-main px-8 py-3 text-xs font-bold uppercase tracking-widest text-surface-card transition-colors duration-300 hover:bg-brand-primary">
|
||||||
Contact Us
|
Contact Us
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ export default async function OrderStatusPage({ params }: { params: Promise<{ id
|
|||||||
<Link href="/track" className="rounded-full bg-surface-card px-6 py-3 font-semibold text-text-main shadow-card transition hover:text-brand-primary">
|
<Link href="/track" className="rounded-full bg-surface-card px-6 py-3 font-semibold text-text-main shadow-card transition hover:text-brand-primary">
|
||||||
ค้นหาออเดอร์อื่น
|
ค้นหาออเดอร์อื่น
|
||||||
</Link>
|
</Link>
|
||||||
<Link href="/shop" className="rounded-full bg-brand-primary px-6 py-3 font-semibold text-white transition hover:bg-brand-primary-hover">
|
<Link href="/shop" className="rounded-full bg-brand-primary px-6 py-3 font-semibold text-surface-card transition hover:bg-brand-primary-hover">
|
||||||
กลับไปช้อปต่อ
|
กลับไปช้อปต่อ
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default async function SearchPage({ searchParams, params }: { searchParam
|
|||||||
placeholder="Search by scent or product name..."
|
placeholder="Search by scent or product name..."
|
||||||
className="min-w-0 flex-1 px-5 py-3 text-sm text-text-main outline-none placeholder:text-muted-text/70"
|
className="min-w-0 flex-1 px-5 py-3 text-sm text-text-main outline-none placeholder:text-muted-text/70"
|
||||||
/>
|
/>
|
||||||
<button className="bg-text-main px-6 py-3 text-sm font-bold uppercase tracking-widest text-white transition hover:bg-brand-primary">
|
<button className="bg-text-main px-6 py-3 text-sm font-bold uppercase tracking-widest text-surface-card transition hover:bg-brand-primary">
|
||||||
Search
|
Search
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default async function ShopPage({ searchParams, params }: { searchParams:
|
|||||||
|
|
||||||
<div className="mx-auto max-w-7xl space-y-10 px-6 py-12">
|
<div className="mx-auto max-w-7xl space-y-10 px-6 py-12">
|
||||||
<div className="flex flex-wrap gap-3">
|
<div className="flex flex-wrap gap-3">
|
||||||
<Link className="rounded-full bg-text-main px-5 py-2 text-xs font-bold uppercase tracking-widest text-white transition hover:bg-brand-primary" href="/shop">
|
<Link className="rounded-full bg-text-main px-5 py-2 text-xs font-bold uppercase tracking-widest text-surface-card transition hover:bg-brand-primary" href="/shop">
|
||||||
All
|
All
|
||||||
</Link>
|
</Link>
|
||||||
{categories.map((category) => {
|
{categories.map((category) => {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
--color-brand-soft: #D8BFD8;
|
--color-brand-soft: #D8BFD8;
|
||||||
--color-surface-page: #FFF0F5;
|
--color-surface-page: #FFF0F5;
|
||||||
--color-surface-nav-rest: #F9F8F5;
|
--color-surface-nav-rest: #F9F8F5;
|
||||||
--color-surface-card: #FFFFFF;
|
--color-surface-card: var(--color-surface-card);
|
||||||
--color-text-main: #333333;
|
--color-text-main: #333333;
|
||||||
--color-muted-text: #666666;
|
--color-muted-text: #666666;
|
||||||
--color-line-primary: #00B900;
|
--color-line-primary: #00B900;
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
.brutal-box {
|
.brutal-box {
|
||||||
@apply bg-white border border-gray-100 rounded-2xl shadow-brutal transition-all duration-300;
|
@apply bg-surface-card border border-line-primary/20 rounded-2xl shadow-brutal transition-all duration-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brutal-box-hover {
|
.brutal-box-hover {
|
||||||
@@ -99,15 +99,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.brutal-button {
|
.brutal-button {
|
||||||
@apply px-8 py-4 bg-brand-primary text-white font-medium rounded-full shadow-brutal transition-all duration-300 hover:bg-brand-primary-hover hover:-translate-y-1 hover:shadow-brutal-hover;
|
@apply px-8 py-4 bg-brand-primary text-surface-card font-medium rounded-full shadow-brutal transition-all duration-300 hover:bg-brand-primary-hover hover:-translate-y-1 hover:shadow-brutal-hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.brutal-button-alt {
|
.brutal-button-alt {
|
||||||
@apply px-8 py-4 bg-white text-brand-primary font-medium rounded-full border border-brand-primary shadow-brutal transition-all duration-300 hover:bg-surface-page hover:-translate-y-1 hover:shadow-brutal-hover;
|
@apply px-8 py-4 bg-surface-card text-brand-primary font-medium rounded-full border border-brand-primary shadow-brutal transition-all duration-300 hover:bg-surface-page hover:-translate-y-1 hover:shadow-brutal-hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-card {
|
.ui-card {
|
||||||
@apply rounded-2xl border border-gray-100 bg-surface-card shadow-card transition-all duration-300;
|
@apply rounded-2xl border border-line-primary/20 bg-surface-card shadow-card transition-all duration-300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-card-hover {
|
.ui-card-hover {
|
||||||
@@ -115,15 +115,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-button-primary {
|
.ui-button-primary {
|
||||||
@apply inline-flex items-center justify-center rounded-full bg-text-main px-6 py-3 text-xs font-bold uppercase tracking-widest text-white transition-colors hover:bg-brand-primary;
|
@apply inline-flex items-center justify-center rounded-full bg-text-main px-6 py-3 text-xs font-bold uppercase tracking-widest text-surface-card transition-colors hover:bg-brand-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-button-brand {
|
.ui-button-brand {
|
||||||
@apply inline-flex items-center justify-center rounded-full bg-brand-primary px-6 py-3 text-xs font-bold uppercase tracking-widest text-white transition-colors hover:bg-brand-primary-hover;
|
@apply inline-flex items-center justify-center rounded-full bg-brand-primary px-6 py-3 text-xs font-bold uppercase tracking-widest text-surface-card transition-colors hover:bg-brand-primary-hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-button-line {
|
.ui-button-line {
|
||||||
@apply inline-flex items-center justify-center rounded-full bg-line-primary px-4 py-2 text-xs font-bold uppercase tracking-widest text-white shadow-sm transition-colors hover:bg-line-primary-hover;
|
@apply inline-flex items-center justify-center rounded-full bg-line-primary px-4 py-2 text-xs font-bold uppercase tracking-widest text-surface-card shadow-sm transition-colors hover:bg-line-primary-hover;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-icon-button {
|
.ui-icon-button {
|
||||||
@@ -135,11 +135,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-nav-chip {
|
.ui-nav-chip {
|
||||||
@apply flex items-center gap-1.5 rounded-full border border-brand-primary/30 bg-white px-3 py-2 text-[10px] font-bold uppercase tracking-wider text-text-main transition-all hover:bg-surface-page hover:text-brand-primary;
|
@apply flex items-center gap-1.5 rounded-full border border-brand-primary/30 bg-surface-card px-3 py-2 text-[10px] font-bold uppercase tracking-wider text-text-main transition-all hover:bg-surface-page hover:text-brand-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-nav-panel {
|
.ui-nav-panel {
|
||||||
@apply overflow-hidden rounded-xl border border-text-main/10 bg-white/95 shadow-2xl backdrop-blur-xl;
|
@apply overflow-hidden rounded-xl border border-text-main/10 bg-surface-card shadow-2xl backdrop-blur-xl;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-menu-link {
|
.ui-menu-link {
|
||||||
@@ -159,11 +159,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-muted-text {
|
.ui-muted-text {
|
||||||
@apply text-gray-600;
|
@apply text-muted-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-input-shell {
|
.ui-input-shell {
|
||||||
@apply rounded-2xl border border-gray-200 bg-surface-page text-text-main outline-none transition focus:border-brand-primary focus:ring-2 focus:ring-brand-primary/20;
|
@apply rounded-2xl border border-line-primary/20 bg-surface-page text-text-main outline-none transition focus:border-brand-primary focus:ring-2 focus:ring-brand-primary/20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-footer-link {
|
.ui-footer-link {
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-footer-social {
|
.ui-footer-social {
|
||||||
@apply flex h-9 w-9 items-center justify-center rounded-full border border-gray-200 bg-surface-page text-[10px] font-bold text-text-main transition-all duration-300 hover:border-brand-primary hover:bg-brand-primary hover:text-white;
|
@apply flex h-9 w-9 items-center justify-center rounded-full border border-line-primary/20 bg-surface-page text-[10px] font-bold text-text-main transition-all duration-300 hover:border-brand-primary hover:bg-brand-primary hover:text-surface-card;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-section-band {
|
.ui-section-band {
|
||||||
@@ -187,11 +187,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-section-subtitle {
|
.ui-section-subtitle {
|
||||||
@apply text-gray-500;
|
@apply text-muted-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-section-card {
|
.ui-section-card {
|
||||||
@apply rounded-3xl bg-white shadow-card;
|
@apply rounded-3xl bg-surface-card shadow-card;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-section-card-muted {
|
.ui-section-card-muted {
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--color-text-main);
|
background: var(--color-text-main);
|
||||||
color: #fff;
|
color: var(--color-surface-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-hero-overlay {
|
.ui-hero-overlay {
|
||||||
@@ -236,7 +236,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-hero-title {
|
.ui-hero-title {
|
||||||
color: #fff;
|
color: var(--color-surface-card);
|
||||||
font-family: var(--font-display-locale);
|
font-family: var(--font-display-locale);
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
@@ -274,7 +274,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
padding: 1rem 2rem;
|
padding: 1rem 2rem;
|
||||||
color: #fff;
|
color: var(--color-surface-card);
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
letter-spacing: 0.1em;
|
letter-spacing: 0.1em;
|
||||||
@@ -308,7 +308,7 @@
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
max-width: 36rem;
|
max-width: 36rem;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
color: #fff;
|
color: var(--color-surface-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-banner-content > * + * {
|
.ui-banner-content > * + * {
|
||||||
@@ -318,7 +318,7 @@
|
|||||||
.ui-section-dark {
|
.ui-section-dark {
|
||||||
background: var(--color-text-main);
|
background: var(--color-text-main);
|
||||||
padding: 5rem 1.5rem;
|
padding: 5rem 1.5rem;
|
||||||
color: #fff;
|
color: var(--color-surface-card);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-section-dark-card {
|
.ui-section-dark-card {
|
||||||
@@ -329,7 +329,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-section-dark-card-title {
|
.ui-section-dark-card-title {
|
||||||
color: #fff;
|
color: var(--color-surface-card);
|
||||||
font-size: 2.25rem;
|
font-size: 2.25rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 2.5rem;
|
line-height: 2.5rem;
|
||||||
@@ -389,7 +389,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-panel {
|
.ui-panel {
|
||||||
@apply rounded-3xl bg-white shadow-card;
|
@apply rounded-3xl bg-surface-card shadow-card;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-panel-muted {
|
.ui-panel-muted {
|
||||||
@@ -397,11 +397,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-panel-border {
|
.ui-panel-border {
|
||||||
@apply rounded-3xl border border-gray-100 bg-white shadow-card;
|
@apply rounded-3xl border border-line-primary/20 bg-surface-card shadow-card;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-panel-header {
|
.ui-panel-header {
|
||||||
@apply flex items-center gap-3 border-b border-gray-100 pb-5;
|
@apply flex items-center gap-3 border-b border-line-primary/20 pb-5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-panel-title {
|
.ui-panel-title {
|
||||||
@@ -409,31 +409,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-panel-subtitle {
|
.ui-panel-subtitle {
|
||||||
@apply text-sm text-gray-500;
|
@apply text-sm text-muted-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-label {
|
.ui-form-label {
|
||||||
@apply text-xs font-bold uppercase tracking-[0.18em] text-gray-500;
|
@apply text-xs font-bold uppercase tracking-[0.18em] text-muted-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-shell {
|
.ui-form-shell {
|
||||||
@apply w-full rounded-2xl border border-gray-200 bg-surface-page px-4 py-3 text-sm text-text-main outline-none transition focus:border-brand-primary focus:ring-2 focus:ring-brand-primary/20;
|
@apply w-full rounded-2xl border border-line-primary/20 bg-surface-page px-4 py-3 text-sm text-text-main outline-none transition focus:border-brand-primary focus:ring-2 focus:ring-brand-primary/20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-shell-light {
|
.ui-form-shell-light {
|
||||||
@apply w-full rounded-2xl border border-gray-200 bg-white px-4 py-3 text-sm text-text-main outline-none transition focus:border-brand-primary focus:ring-2 focus:ring-brand-primary/20;
|
@apply w-full rounded-2xl border border-line-primary/20 bg-surface-card px-4 py-3 text-sm text-text-main outline-none transition focus:border-brand-primary focus:ring-2 focus:ring-brand-primary/20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-inline-button {
|
.ui-form-inline-button {
|
||||||
@apply inline-flex items-center justify-center gap-2 rounded-full bg-text-main px-6 py-4 text-sm font-bold uppercase tracking-[0.16em] text-white transition hover:bg-brand-primary disabled:cursor-not-allowed disabled:opacity-60;
|
@apply inline-flex items-center justify-center gap-2 rounded-full bg-text-main px-6 py-4 text-sm font-bold uppercase tracking-[0.16em] text-surface-card transition hover:bg-brand-primary disabled:cursor-not-allowed disabled:opacity-60;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-inline-button-sm {
|
.ui-form-inline-button-sm {
|
||||||
@apply inline-flex items-center justify-center gap-2 rounded-full bg-text-main px-5 py-2 text-sm font-semibold text-white transition hover:bg-brand-primary disabled:cursor-not-allowed disabled:opacity-60;
|
@apply inline-flex items-center justify-center gap-2 rounded-full bg-text-main px-5 py-2 text-sm font-semibold text-surface-card transition hover:bg-brand-primary disabled:cursor-not-allowed disabled:opacity-60;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-secondary-button {
|
.ui-form-secondary-button {
|
||||||
@apply rounded-full border border-gray-200 px-5 py-2 text-sm font-semibold text-gray-600 transition hover:text-brand-primary;
|
@apply rounded-full border border-line-primary/20 px-5 py-2 text-sm font-semibold text-muted-text transition hover:text-brand-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-danger-button {
|
.ui-form-danger-button {
|
||||||
@@ -441,7 +441,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-form-checkbox {
|
.ui-form-checkbox {
|
||||||
@apply h-4 w-4 rounded border-gray-300 text-brand-primary;
|
@apply h-4 w-4 rounded border-line-primary/20 text-brand-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-status-badge {
|
.ui-status-badge {
|
||||||
@@ -523,7 +523,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab-button-inactive {
|
.ui-tab-button-inactive {
|
||||||
@apply text-gray-500 hover:text-text-main;
|
@apply text-muted-text hover:text-text-main;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-tab-count {
|
.ui-tab-count {
|
||||||
@@ -531,7 +531,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ui-empty-panel {
|
.ui-empty-panel {
|
||||||
@apply rounded-3xl bg-white px-6 py-16 text-center shadow-card;
|
@apply rounded-3xl bg-surface-card px-6 py-16 text-center shadow-card;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,11 +545,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
::-webkit-scrollbar-thumb {
|
||||||
@apply bg-gray-300 rounded-full;
|
@apply bg-surface-nav-rest rounded-full;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
::-webkit-scrollbar-thumb:hover {
|
||||||
@apply bg-gray-400;
|
@apply bg-surface-nav-rest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Smooth Scrolling */
|
/* Smooth Scrolling */
|
||||||
|
|||||||
@@ -70,12 +70,12 @@ export default function AddToCartSection({ product }: Props) {
|
|||||||
onClick={() => setSelectedVariant(index)}
|
onClick={() => setSelectedVariant(index)}
|
||||||
className={`rounded-xl border px-5 py-3 text-sm font-bold uppercase tracking-widest transition ${
|
className={`rounded-xl border px-5 py-3 text-sm font-bold uppercase tracking-widest transition ${
|
||||||
selectedVariant === index
|
selectedVariant === index
|
||||||
? 'border-brand-primary bg-brand-primary text-white shadow-card'
|
? 'border-brand-primary bg-brand-primary text-surface-card shadow-card'
|
||||||
: 'border-line-primary/20 bg-surface-card text-muted-text hover:border-brand-primary hover:text-brand-primary'
|
: 'border-line-primary/20 bg-surface-card text-muted-text hover:border-brand-primary hover:text-brand-primary'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{variant.size}
|
{variant.size}
|
||||||
<span className={`mt-1 block text-[10px] ${selectedVariant === index ? 'text-white/80' : 'text-muted-text/70'}`}>
|
<span className={`mt-1 block text-[10px] ${selectedVariant === index ? 'text-surface-card' : 'text-muted-text/70'}`}>
|
||||||
{formatPrice(variant.price)}
|
{formatPrice(variant.price)}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -124,7 +124,7 @@ export default function AddToCartSection({ product }: Props) {
|
|||||||
onClick={() => setSelectedVariant(index)}
|
onClick={() => setSelectedVariant(index)}
|
||||||
className={`flex-shrink-0 rounded-full border px-4 py-2 text-[10px] font-bold uppercase tracking-widest transition ${
|
className={`flex-shrink-0 rounded-full border px-4 py-2 text-[10px] font-bold uppercase tracking-widest transition ${
|
||||||
selectedVariant === index
|
selectedVariant === index
|
||||||
? 'border-brand-primary bg-brand-primary text-white'
|
? 'border-brand-primary bg-brand-primary text-surface-card'
|
||||||
: 'border-line-primary/20 bg-surface-card text-muted-text'
|
: 'border-line-primary/20 bg-surface-card text-muted-text'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export default function CartDrawer() {
|
|||||||
<span className="text-brand-primary">{formatPrice(getCartTotal())}</span>
|
<span className="text-brand-primary">{formatPrice(getCartTotal())}</span>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-center text-xs text-muted-text">{t('cart.shippingNote')}</p>
|
<p className="text-center text-xs text-muted-text">{t('cart.shippingNote')}</p>
|
||||||
<Link href="/checkout" onClick={() => toggleCart(false)} className="block w-full rounded-full bg-text-main py-4 text-center text-sm font-bold uppercase tracking-widest text-white transition hover:bg-brand-primary">
|
<Link href="/checkout" onClick={() => toggleCart(false)} className="block w-full rounded-full bg-text-main py-4 text-center text-sm font-bold uppercase tracking-widest text-surface-card transition hover:bg-brand-primary">
|
||||||
{t('cart.proceed')}
|
{t('cart.proceed')}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ export default function CheckoutClient() {
|
|||||||
<ShoppingBag className="mx-auto h-14 w-14 text-brand-primary" />
|
<ShoppingBag className="mx-auto h-14 w-14 text-brand-primary" />
|
||||||
<h2 className="mt-5 text-3xl font-semibold text-text-main">ตะกร้าสินค้าว่าง</h2>
|
<h2 className="mt-5 text-3xl font-semibold text-text-main">ตะกร้าสินค้าว่าง</h2>
|
||||||
<p className="mt-3 text-muted-text">เลือกสินค้าที่คุณชอบก่อน แล้วกลับมายืนยันคำสั่งซื้อที่หน้านี้ได้เลย</p>
|
<p className="mt-3 text-muted-text">เลือกสินค้าที่คุณชอบก่อน แล้วกลับมายืนยันคำสั่งซื้อที่หน้านี้ได้เลย</p>
|
||||||
<Link href="/shop" className="mt-8 inline-flex rounded-full bg-text-main px-8 py-4 text-sm font-bold uppercase tracking-[0.16em] text-white transition hover:bg-brand-primary">
|
<Link href="/shop" className="mt-8 inline-flex rounded-full bg-text-main px-8 py-4 text-sm font-bold uppercase tracking-[0.16em] text-surface-card transition hover:bg-brand-primary">
|
||||||
กลับไปเลือกสินค้า
|
กลับไปเลือกสินค้า
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@@ -642,7 +642,7 @@ export default function CheckoutClient() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={applyCoupon}
|
onClick={applyCoupon}
|
||||||
disabled={couponLoading || !couponCode.trim()}
|
disabled={couponLoading || !couponCode.trim()}
|
||||||
className="rounded-2xl bg-text-main px-4 py-3 text-xs font-bold uppercase tracking-widest text-white transition hover:bg-brand-primary disabled:opacity-50"
|
className="rounded-2xl bg-text-main px-4 py-3 text-xs font-bold uppercase tracking-widest text-surface-card transition hover:bg-brand-primary disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{couponLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : 'ใช้'}
|
{couponLoading ? <Loader2 className="h-4 w-4 animate-spin" /> : 'ใช้'}
|
||||||
</button>
|
</button>
|
||||||
@@ -756,7 +756,7 @@ export default function CheckoutClient() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
form="checkout-form"
|
form="checkout-form"
|
||||||
disabled={submitting || loadingSettings}
|
disabled={submitting || loadingSettings}
|
||||||
className="hidden w-full items-center justify-between rounded-full bg-text-main px-6 py-4 text-sm font-bold uppercase tracking-[0.16em] text-white transition hover:bg-brand-primary disabled:cursor-not-allowed disabled:opacity-60 lg:flex"
|
className="hidden w-full items-center justify-between rounded-full bg-text-main px-6 py-4 text-sm font-bold uppercase tracking-[0.16em] text-surface-card transition hover:bg-brand-primary disabled:cursor-not-allowed disabled:opacity-60 lg:flex"
|
||||||
>
|
>
|
||||||
<span className="flex items-center gap-2">
|
<span className="flex items-center gap-2">
|
||||||
{submitting && <Loader2 className="h-5 w-5 animate-spin" />}
|
{submitting && <Loader2 className="h-5 w-5 animate-spin" />}
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
|
|||||||
>
|
>
|
||||||
<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-white">
|
<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>
|
||||||
)}
|
)}
|
||||||
@@ -247,7 +247,7 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
|
|||||||
>
|
>
|
||||||
<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-white">
|
<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>
|
||||||
)}
|
)}
|
||||||
@@ -353,7 +353,7 @@ export default function Navbar({ categories = [], initialMenu = [], settings = n
|
|||||||
className="w-full border-none bg-transparent p-4 pl-0 text-lg text-text-main outline-none placeholder:text-text-main/35 md:text-xl"
|
className="w-full border-none bg-transparent p-4 pl-0 text-lg text-text-main outline-none placeholder:text-text-main/35 md:text-xl"
|
||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<button type="submit" className="shrink-0 bg-text-main px-4 text-xs font-bold uppercase tracking-widest text-white transition-colors hover:bg-brand-primary md:px-6">
|
<button type="submit" className="shrink-0 bg-text-main px-4 text-xs font-bold uppercase tracking-widest text-surface-card transition-colors hover:bg-brand-primary md:px-6">
|
||||||
Go
|
Go
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ export default function ProfileClient() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleLineLogin}
|
onClick={handleLineLogin}
|
||||||
className="mt-8 flex w-full items-center justify-center gap-3 rounded-full bg-line-primary px-6 py-4 text-sm font-bold uppercase tracking-[0.16em] text-white transition hover:bg-line-primary-hover"
|
className="mt-8 flex w-full items-center justify-center gap-3 rounded-full bg-line-primary px-6 py-4 text-sm font-bold uppercase tracking-[0.16em] text-surface-card transition hover:bg-line-primary-hover"
|
||||||
>
|
>
|
||||||
<Award className="h-5 w-5" />
|
<Award className="h-5 w-5" />
|
||||||
เข้าสู่ระบบผ่าน LINE
|
เข้าสู่ระบบผ่าน LINE
|
||||||
@@ -435,7 +435,7 @@ export default function ProfileClient() {
|
|||||||
สมุดที่อยู่จัดส่ง
|
สมุดที่อยู่จัดส่ง
|
||||||
</h2>
|
</h2>
|
||||||
{!showAddressForm && (
|
{!showAddressForm && (
|
||||||
<button type="button" onClick={() => handleStartAddress()} className="inline-flex items-center justify-center gap-2 rounded-full bg-text-main px-5 py-3 text-xs font-bold uppercase tracking-[0.16em] text-white transition hover:bg-brand-primary">
|
<button type="button" onClick={() => handleStartAddress()} className="inline-flex items-center justify-center gap-2 rounded-full bg-text-main px-5 py-3 text-xs font-bold uppercase tracking-[0.16em] text-surface-card transition hover:bg-brand-primary">
|
||||||
<Plus className="h-4 w-4" />
|
<Plus className="h-4 w-4" />
|
||||||
เพิ่มที่อยู่ใหม่
|
เพิ่มที่อยู่ใหม่
|
||||||
</button>
|
</button>
|
||||||
@@ -474,7 +474,7 @@ export default function ProfileClient() {
|
|||||||
<button type="button" onClick={resetAddressForm} className="ui-form-secondary-button">
|
<button type="button" onClick={resetAddressForm} className="ui-form-secondary-button">
|
||||||
ยกเลิก
|
ยกเลิก
|
||||||
</button>
|
</button>
|
||||||
<button type="submit" disabled={submittingAddress} className="inline-flex items-center gap-2 rounded-full bg-brand-primary px-5 py-2 text-sm font-semibold text-white disabled:opacity-60">
|
<button type="submit" disabled={submittingAddress} className="inline-flex items-center gap-2 rounded-full bg-brand-primary px-5 py-2 text-sm font-semibold text-surface-card disabled:opacity-60">
|
||||||
{submittingAddress && <Loader2 className="h-4 w-4 animate-spin" />}
|
{submittingAddress && <Loader2 className="h-4 w-4 animate-spin" />}
|
||||||
บันทึกที่อยู่
|
บันทึกที่อยู่
|
||||||
</button>
|
</button>
|
||||||
@@ -534,7 +534,7 @@ export default function ProfileClient() {
|
|||||||
)}
|
)}
|
||||||
<TextInput label="อีเมล" value={settingsForm.email} onChange={(email) => setSettingsForm((current) => ({ ...current, email }))} type="email" />
|
<TextInput label="อีเมล" value={settingsForm.email} onChange={(email) => setSettingsForm((current) => ({ ...current, email }))} type="email" />
|
||||||
<TextInput label="เบอร์โทรศัพท์" value={settingsForm.phone} onChange={(phone) => setSettingsForm((current) => ({ ...current, phone }))} type="tel" />
|
<TextInput label="เบอร์โทรศัพท์" value={settingsForm.phone} onChange={(phone) => setSettingsForm((current) => ({ ...current, phone }))} type="tel" />
|
||||||
<button type="submit" disabled={submittingSettings} className="inline-flex w-full items-center justify-center gap-2 rounded-full bg-brand-primary px-6 py-3 text-sm font-bold uppercase tracking-[0.16em] text-white disabled:opacity-60">
|
<button type="submit" disabled={submittingSettings} className="inline-flex w-full items-center justify-center gap-2 rounded-full bg-brand-primary px-6 py-3 text-sm font-bold uppercase tracking-[0.16em] text-surface-card disabled:opacity-60">
|
||||||
{submittingSettings ? <Loader2 className="h-4 w-4 animate-spin" /> : <Check className="h-4 w-4" />}
|
{submittingSettings ? <Loader2 className="h-4 w-4 animate-spin" /> : <Check className="h-4 w-4" />}
|
||||||
บันทึกข้อมูลติดต่อ
|
บันทึกข้อมูลติดต่อ
|
||||||
</button>
|
</button>
|
||||||
@@ -598,7 +598,7 @@ function EmptyPanel({
|
|||||||
<h3 className="text-xl font-semibold text-text-main">{title}</h3>
|
<h3 className="text-xl font-semibold text-text-main">{title}</h3>
|
||||||
<p className="mx-auto mt-2 max-w-md text-sm text-muted-text">{description}</p>
|
<p className="mx-auto mt-2 max-w-md text-sm text-muted-text">{description}</p>
|
||||||
{actionHref && actionText && (
|
{actionHref && actionText && (
|
||||||
<Link href={actionHref} className="mt-6 inline-flex rounded-full bg-brand-primary px-6 py-3 text-sm font-bold text-white">
|
<Link href={actionHref} className="mt-6 inline-flex rounded-full bg-brand-primary px-6 py-3 text-sm font-bold text-surface-card">
|
||||||
{actionText}
|
{actionText}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ export default function TrackOrderClient() {
|
|||||||
<p className="text-xs font-bold uppercase tracking-[0.22em] text-muted-text/70">Order</p>
|
<p className="text-xs font-bold uppercase tracking-[0.22em] text-muted-text/70">Order</p>
|
||||||
<p className="mt-1 font-mono text-2xl font-bold text-text-main">{orderLabel(id)}</p>
|
<p className="mt-1 font-mono text-2xl font-bold text-text-main">{orderLabel(id)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-surface-page text-text-main transition group-hover:bg-brand-primary group-hover:text-white">
|
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-surface-page text-text-main transition group-hover:bg-brand-primary group-hover:text-surface-card">
|
||||||
<ChevronRight className="h-5 w-5" />
|
<ChevronRight className="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default function WishlistClient() {
|
|||||||
</p>
|
</p>
|
||||||
<Link
|
<Link
|
||||||
href="/shop"
|
href="/shop"
|
||||||
className="mt-8 inline-flex rounded-full bg-brand-primary px-8 py-3 text-sm font-bold uppercase tracking-[0.18em] text-white transition hover:bg-brand-primary-hover"
|
className="mt-8 inline-flex rounded-full bg-brand-primary px-8 py-3 text-sm font-bold uppercase tracking-[0.18em] text-surface-card transition hover:bg-brand-primary-hover"
|
||||||
>
|
>
|
||||||
Browse Shop
|
Browse Shop
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ function AnnouncementBar({ payload, lang }: { payload: Record<string, unknown>,
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
className={`announcement-bar absolute left-0 top-0 z-[60] w-full overflow-hidden px-4 py-3 text-center text-sm font-semibold tracking-wide ${backgroundColor ? '' : 'bg-text-main'} ${textColor ? '' : 'text-white'}`}
|
className={`announcement-bar absolute left-0 top-0 z-[60] w-full overflow-hidden px-4 py-3 text-center text-sm font-semibold tracking-wide ${backgroundColor ? '' : 'bg-text-main'} ${textColor ? '' : 'text-surface-card'}`}
|
||||||
style={{ backgroundColor, color: textColor }}
|
style={{ backgroundColor, color: textColor }}
|
||||||
>
|
>
|
||||||
<Swiper
|
<Swiper
|
||||||
@@ -125,7 +125,7 @@ function HeroSection({ payload, lang }: { payload: Record<string, unknown>, lang
|
|||||||
<div className="relative z-10 mx-auto mt-16 w-full max-w-4xl space-y-6 px-8 text-center md:mt-0">
|
<div className="relative z-10 mx-auto mt-16 w-full max-w-4xl space-y-6 px-8 text-center md:mt-0">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{tagline && (
|
{tagline && (
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.3em] text-white drop-shadow-md md:text-sm">
|
<p className="text-xs font-semibold uppercase tracking-[0.3em] text-surface-card drop-shadow-md md:text-sm">
|
||||||
{tagline}
|
{tagline}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
@@ -209,10 +209,10 @@ function CategoriesSection({
|
|||||||
className="absolute inset-0 h-full w-full object-cover transition duration-700 group-hover:scale-105"
|
className="absolute inset-0 h-full w-full object-cover transition duration-700 group-hover:scale-105"
|
||||||
/>
|
/>
|
||||||
<div className="absolute inset-0 bg-gradient-to-t from-text-main/65 to-transparent" />
|
<div className="absolute inset-0 bg-gradient-to-t from-text-main/65 to-transparent" />
|
||||||
<div className="absolute bottom-5 left-5 right-5 text-white">
|
<div className="absolute bottom-5 left-5 right-5 text-surface-card">
|
||||||
<span className="block text-lg font-semibold">{name}</span>
|
<span className="block text-lg font-semibold">{name}</span>
|
||||||
{category._count && (
|
{category._count && (
|
||||||
<span className="text-sm text-white/80">
|
<span className="text-sm text-surface-card">
|
||||||
{category._count.products} {productsLabel[resolvedLang] ?? productsLabel.en}
|
{category._count.products} {productsLabel[resolvedLang] ?? productsLabel.en}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -290,7 +290,7 @@ function BannerSection({ payload, lang }: { payload: Record<string, unknown>, la
|
|||||||
<div className="ui-banner-overlay" />
|
<div className="ui-banner-overlay" />
|
||||||
<div className="ui-banner-content">
|
<div className="ui-banner-content">
|
||||||
{title && <h2 className="text-3xl font-semibold md:text-5xl">{title}</h2>}
|
{title && <h2 className="text-3xl font-semibold md:text-5xl">{title}</h2>}
|
||||||
{description && <p className="text-white/90">{description}</p>}
|
{description && <p className="text-surface-card">{description}</p>}
|
||||||
{buttonText && <span className="inline-flex rounded-full bg-surface-card px-6 py-3 font-semibold text-text-main">{buttonText}</span>}
|
{buttonText && <span className="inline-flex rounded-full bg-surface-card px-6 py-3 font-semibold text-text-main">{buttonText}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -332,8 +332,8 @@ function NewsletterSection({ payload, lang }: { payload: Record<string, unknown>
|
|||||||
<section className="ui-section-dark">
|
<section className="ui-section-dark">
|
||||||
<div className="mx-auto grid max-w-5xl gap-8 md:grid-cols-[1fr_auto] md:items-end">
|
<div className="mx-auto grid max-w-5xl gap-8 md:grid-cols-[1fr_auto] md:items-end">
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<h2 className="text-3xl font-semibold text-white md:text-4xl">{title}</h2>
|
<h2 className="text-3xl font-semibold text-surface-card md:text-4xl">{title}</h2>
|
||||||
{description && <p className="max-w-2xl text-white/75">{description}</p>}
|
{description && <p className="max-w-2xl text-surface-card">{description}</p>}
|
||||||
</div>
|
</div>
|
||||||
<form className="flex flex-col gap-3 sm:flex-row">
|
<form className="flex flex-col gap-3 sm:flex-row">
|
||||||
<input className="ui-form-shell min-w-0 rounded-full" placeholder={placeholder} type="email" />
|
<input className="ui-form-shell min-w-0 rounded-full" placeholder={placeholder} type="email" />
|
||||||
@@ -381,7 +381,7 @@ function EditorialTileSection({ payload, lang }: { payload: Record<string, unkno
|
|||||||
<div className="h-px w-14 bg-brand-primary" />
|
<div className="h-px w-14 bg-brand-primary" />
|
||||||
{description && <p className="whitespace-pre-line text-base leading-relaxed text-muted-text md:text-lg" style={{ color: textColor }}>{description}</p>}
|
{description && <p className="whitespace-pre-line text-base leading-relaxed text-muted-text md:text-lg" style={{ color: textColor }}>{description}</p>}
|
||||||
{buttonText && buttonLink && (
|
{buttonText && buttonLink && (
|
||||||
<Link href={buttonLink} className="ui-section-link inline-flex rounded-full border border-current px-8 py-3 text-xs font-bold uppercase tracking-[0.22em] transition hover:bg-brand-primary hover:text-white">
|
<Link href={buttonLink} className="ui-section-link inline-flex rounded-full border border-current px-8 py-3 text-xs font-bold uppercase tracking-[0.22em] transition hover:bg-brand-primary hover:text-surface-card">
|
||||||
{buttonText}
|
{buttonText}
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
@@ -403,7 +403,7 @@ function EditorialCarouselSection({ payload, lang }: { payload: Record<string, u
|
|||||||
<div className="mx-auto max-w-7xl space-y-12">
|
<div className="mx-auto max-w-7xl space-y-12">
|
||||||
{title && (
|
{title && (
|
||||||
<div className="mx-auto max-w-3xl space-y-4 text-center">
|
<div className="mx-auto max-w-3xl space-y-4 text-center">
|
||||||
<h2 className="text-4xl font-semibold text-white md:text-5xl">{renderAccentTitle(title, 'ui-accent-text not-italic')}</h2>
|
<h2 className="text-4xl font-semibold text-surface-card md:text-5xl">{renderAccentTitle(title, 'ui-accent-text not-italic')}</h2>
|
||||||
<div className="mx-auto h-px w-16 bg-brand-accent" />
|
<div className="mx-auto h-px w-16 bg-brand-accent" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -485,9 +485,9 @@ function HeroArticleSection({ payload, lang }: { payload: Record<string, unknown
|
|||||||
<div className="relative z-10 mx-auto max-w-4xl space-y-6">
|
<div className="relative z-10 mx-auto max-w-4xl space-y-6">
|
||||||
{category && <p className="text-xs font-bold uppercase tracking-[0.28em] text-brand-accent">{category}</p>}
|
{category && <p className="text-xs font-bold uppercase tracking-[0.28em] text-brand-accent">{category}</p>}
|
||||||
<h1 className="ui-hero-title">{title}</h1>
|
<h1 className="ui-hero-title">{title}</h1>
|
||||||
{subtitle && <p className="mx-auto max-w-2xl text-lg text-white/85 md:text-xl">{subtitle}</p>}
|
{subtitle && <p className="mx-auto max-w-2xl text-lg text-surface-card md:text-xl">{subtitle}</p>}
|
||||||
{(author || date) && (
|
{(author || date) && (
|
||||||
<p className="text-xs font-bold uppercase tracking-[0.22em] text-white/70">
|
<p className="text-xs font-bold uppercase tracking-[0.22em] text-surface-card">
|
||||||
{[author && `${authorPrefix[resolvedLang] ?? authorPrefix.en} ${author}`, date].filter(Boolean).join(' / ')}
|
{[author && `${authorPrefix[resolvedLang] ?? authorPrefix.en} ${author}`, date].filter(Boolean).join(' / ')}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export function MobileDrawer({
|
|||||||
<Link
|
<Link
|
||||||
href="/shop"
|
href="/shop"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="block w-full rounded-xl bg-text-main py-3.5 text-center text-xs font-bold uppercase tracking-widest text-white shadow-sm transition-colors hover:bg-brand-primary"
|
className="block w-full rounded-xl bg-text-main py-3.5 text-center text-xs font-bold uppercase tracking-widest text-surface-card shadow-sm transition-colors hover:bg-brand-primary"
|
||||||
>
|
>
|
||||||
Shop Now
|
Shop Now
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
Reference in new issue
Block a user