x
This commit is contained in:
1 parent
c449de2482
commit
7e4e89e33c
50 files changed
+306
-140
No files matched your search
@@ -68,7 +68,7 @@ export default function CartDrawer() {
|
||||
<div key={item.cartItemId} className="flex gap-4 p-4 rounded-2xl bg-white 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' }}>
|
||||
<img
|
||||
src={item.productImage || `${import.meta.env.VITE_API_URL.replace('/api', '')}/assets/oil_ozone.png`}
|
||||
src={item.productImage || '/images/fallback-product.svg'}
|
||||
alt={item.productName}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
@@ -151,7 +151,7 @@ function CartDrawerFooter({ subtotal, onClose, t }: { subtotal: number; onClose:
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
const code = localStorage.getItem('homaroma_active_coupon')
|
||||
const code = localStorage.getItem('app_active_coupon')
|
||||
setSavedCoupon(code)
|
||||
if (code && subtotal > 0) {
|
||||
fetch(`${API_BASE}/coupons/validate`, {
|
||||
@@ -174,7 +174,7 @@ function CartDrawerFooter({ subtotal, onClose, t }: { subtotal: number; onClose:
|
||||
}, [subtotal, activePromos])
|
||||
|
||||
const removeCoupon = () => {
|
||||
localStorage.removeItem('homaroma_active_coupon')
|
||||
localStorage.removeItem('app_active_coupon')
|
||||
setSavedCoupon(null)
|
||||
setDiscountAmount(0)
|
||||
setIsFreeShipping(false)
|
||||
@@ -190,7 +190,7 @@ function CartDrawerFooter({ subtotal, onClose, t }: { subtotal: number; onClose:
|
||||
|
||||
// We must validate with subtotalAfterL2, but since we don't have it defined here yet,
|
||||
// we'll calculate it inline or rely on the effect. Actually, we can just save it to localStorage and let the effect handle it.
|
||||
localStorage.setItem('homaroma_active_coupon', couponInput.toUpperCase())
|
||||
localStorage.setItem('app_active_coupon', couponInput.toUpperCase())
|
||||
setSavedCoupon(couponInput.toUpperCase())
|
||||
setCouponInput('')
|
||||
setCouponLoading(false)
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function DynamicCategories({ payload, categories, loading }: Dyna
|
||||
/>
|
||||
<div className="absolute bottom-6 left-6 right-6 z-20 text-white">
|
||||
<span className="mb-1 block text-xl font-semibold">
|
||||
{getLocalizedField(cat.name, localStorage.getItem('homaroma_lang') || 'en')}
|
||||
{getLocalizedField(cat.name, localStorage.getItem('app_lang') || 'en')}
|
||||
</span>
|
||||
{cat._count && (
|
||||
<span className="text-sm font-medium text-white/80">
|
||||
|
||||
@@ -24,7 +24,7 @@ export default function Footer({ categories = [] }: FooterProps) {
|
||||
const brandName = settings?.brandName || ''
|
||||
|
||||
const translateMenuLabel = (item: MenuItemWithTranslations) => {
|
||||
const lang = localStorage.getItem('homaroma_lang') || 'en'
|
||||
const lang = localStorage.getItem('app_lang') || 'en'
|
||||
if (lang !== 'en' && item.translations && item.translations.length > 0) {
|
||||
const trans = item.translations.find((translation) => translation.langCode === lang)
|
||||
if (trans && trans.label) {
|
||||
@@ -128,7 +128,7 @@ export default function Footer({ categories = [] }: FooterProps) {
|
||||
<div className="flex flex-col space-y-2 text-sm text-slate-400">
|
||||
{categories.map((c) => (
|
||||
<Link key={c.id} to={`/category/${c.id}`} className="hover:text-white transition-colors">
|
||||
{getLocalizedField(c.name, localStorage.getItem('homaroma_lang') || 'en')}
|
||||
{getLocalizedField(c.name, localStorage.getItem('app_lang') || 'en')}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
||||
|
||||
const changeLanguage = (code: string) => {
|
||||
i18n.changeLanguage(code)
|
||||
localStorage.setItem('homaroma_lang', code)
|
||||
localStorage.setItem('app_lang', code)
|
||||
setLangSheetOpen(false)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { useEffect } from 'react'
|
||||
import { API_BASE } from '@homaroma/types'
|
||||
|
||||
export default function ThemeInjector() {
|
||||
useEffect(() => {
|
||||
const lang = localStorage.getItem('app_lang') || 'en'
|
||||
|
||||
fetch(`${API_BASE}/settings?lang=${lang}`)
|
||||
.then(res => res.ok ? res.json() : null)
|
||||
.then(settings => {
|
||||
const tokens = settings?.themeTokens
|
||||
if (!tokens || typeof tokens !== 'object') return
|
||||
|
||||
const root = document.documentElement
|
||||
|
||||
// Map tokens from the backend (which use brandPrimary etc.)
|
||||
// to the CSS variables expected by SPA's tailwind.config.js
|
||||
const mapping: Record<string, string> = {
|
||||
'--color-spa-light': tokens.surfacePage,
|
||||
'--color-spa-sage': tokens.brandSoft,
|
||||
'--color-spa-stone': tokens.mutedText,
|
||||
'--color-spa-forest': tokens.brandPrimary,
|
||||
'--color-spa-gold': tokens.brandAccent,
|
||||
|
||||
'--color-pop-pink': tokens.brandPrimary,
|
||||
'--color-pop-yellow': tokens.brandAccent,
|
||||
'--color-pop-orange': tokens.brandWarm,
|
||||
'--color-pop-cyan': tokens.brandCool,
|
||||
'--color-pop-bg': tokens.surfacePage,
|
||||
'--color-pop-black': tokens.textMain,
|
||||
|
||||
'--color-night-900': tokens.surfaceCard,
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(mapping)) {
|
||||
if (value && typeof value === 'string' && value.trim() !== '') {
|
||||
root.style.setProperty(key, value)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('Failed to inject theme in SPA:', err))
|
||||
}, [])
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in new issue
Block a user