This commit is contained in:
Your Name committed 2026-06-21 01:52:57 -04:00
1 parent c449de2482
commit 7e4e89e33c
50 files changed
+306 -140

No files matched your search

+4 -4
View File
@@ -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">
+2 -2
View File
@@ -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>
+1 -1
View File
@@ -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)
}
+45
View File
@@ -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
}