x
This commit is contained in:
1 parent
862eeef48b
commit
5c09629b6f
12 files changed
+135
-47
No files matched your search
@@ -15,6 +15,7 @@ export default function DynamicHero({ payload }: DynamicHeroProps) {
|
||||
const title = getLocalizedField(payload.title, lang);
|
||||
const description = getLocalizedField(payload.description, lang);
|
||||
const buttonText = getLocalizedField(payload.buttonText, lang);
|
||||
const focalPoint = typeof payload.focalPoint === 'object' && payload.focalPoint ? (payload.focalPoint as { x: string, y: string }) : undefined;
|
||||
|
||||
const renderTitle = (text: string) => {
|
||||
if (!text) return null;
|
||||
@@ -41,6 +42,7 @@ export default function DynamicHero({ payload }: DynamicHeroProps) {
|
||||
src={imageUrl || `${import.meta.env.VITE_API_URL.replace('/api', '')}/assets/hero.png`}
|
||||
className="w-full h-full object-cover"
|
||||
alt="Hero Background"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
onError={(e) => {
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
|
||||
@@ -23,6 +23,7 @@ export default function DynamicHeroV2({ payload }: DynamicHeroV2Props) {
|
||||
const imageUrl = typeof payload.imageUrl === 'string' ? payload.imageUrl : '/assets/hero.png';
|
||||
const align = typeof payload.contentAlignment === 'string' ? payload.contentAlignment : 'left';
|
||||
const showOrnament = typeof payload.showOrnament === 'boolean' ? payload.showOrnament : true;
|
||||
const focalPoint = typeof payload.focalPoint === 'object' && payload.focalPoint ? (payload.focalPoint as { x: string, y: string }) : undefined;
|
||||
|
||||
const alignClass = align === 'center' ? 'text-center items-center mx-auto' : align === 'right' ? 'text-right items-end ml-auto' : 'text-left items-start';
|
||||
|
||||
@@ -51,6 +52,7 @@ export default function DynamicHeroV2({ payload }: DynamicHeroV2Props) {
|
||||
src={imageUrl}
|
||||
alt={title || 'Hero'}
|
||||
className="w-full h-full object-cover"
|
||||
style={{ objectPosition: focalPoint ? `${focalPoint.x} ${focalPoint.y}` : undefined }}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-white/90 via-white/50 to-transparent pointer-events-none" />
|
||||
</div>
|
||||
|
||||
@@ -15,9 +15,10 @@ import { useMenuStore } from '../store/menuStore'
|
||||
interface NavbarProps {
|
||||
categories?: Category[]
|
||||
topOffsetClassName?: string
|
||||
isTransparent?: boolean
|
||||
}
|
||||
|
||||
export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
||||
export default function Navbar({ topOffsetClassName = 'top-0', isTransparent = false }: NavbarProps) {
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)')
|
||||
|
||||
const [searchOpen, setSearchOpen] = useState(false)
|
||||
@@ -147,12 +148,18 @@ export default function Navbar({ topOffsetClassName = 'top-0' }: NavbarProps) {
|
||||
|
||||
const logoUrl = settings?.logoUrl || ''
|
||||
|
||||
const shouldBeTransparent = isTransparent && !scrolled && !mobileOpen && !searchOpen && !langSheetOpen
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav
|
||||
ref={navRef}
|
||||
className={`fixed ${finalTopClassName} z-50 w-full border-b border-pop-black/10 transition-all duration-300 ${
|
||||
scrolled ? 'shadow-lg shadow-pop-black/10 backdrop-blur-xl bg-pop-bg/90' : 'shadow-none bg-pop-bg'
|
||||
className={`fixed ${finalTopClassName} z-50 w-full transition-all duration-300 ${
|
||||
scrolled
|
||||
? 'border-b border-pop-black/10 shadow-lg shadow-pop-black/10 backdrop-blur-xl bg-pop-bg/90'
|
||||
: shouldBeTransparent
|
||||
? 'border-transparent shadow-none bg-transparent backdrop-blur-none'
|
||||
: 'border-b border-pop-black/10 shadow-none bg-pop-bg'
|
||||
} px-4 py-3.5 md:px-6 md:py-4`}
|
||||
>
|
||||
{/* Single-row layout: Logo (left) | Menu (center, desktop) | Actions (right) */}
|
||||
|
||||
@@ -119,42 +119,42 @@ export default function CategoryPage() {
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="relative h-64 md:h-80 overflow-hidden">
|
||||
<img
|
||||
src={getCategoryImage(typeof category?.name === 'string' ? category.name : (category?.name as any)?.en || '')}
|
||||
className="absolute inset-0 w-full h-full object-cover brightness-30 scale-105"
|
||||
alt={typeof category?.name === 'string' ? category.name : (category?.name as any)?.[i18n.language] || (category?.name as any)?.en || ''}
|
||||
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }}
|
||||
/>
|
||||
<div className="absolute inset-0 bg-night-900/40" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-night-900/90 via-night-900/10 to-transparent" />
|
||||
<div className="absolute bottom-0 left-0 right-0 px-6 pb-10 pt-20">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
<div className="bg-spa-stone/5 border-b border-spa-stone/10 pt-32 pb-16 px-6">
|
||||
<div className="max-w-7xl mx-auto flex flex-col-reverse md:flex-row items-center gap-10 md:gap-16">
|
||||
<div className="flex-1 text-center md:text-left">
|
||||
{/* Breadcrumb */}
|
||||
<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-spa-light transition-colors">Home</Link>
|
||||
<div className="flex items-center justify-center md:justify-start gap-2 text-xs text-spa-stone/60 uppercase tracking-widest mb-4 font-semibold">
|
||||
<Link to="/" className="hover:text-spa-forest transition-colors">Home</Link>
|
||||
<span>/</span>
|
||||
<Link to="/shop" className="hover:text-spa-light transition-colors">Shop</Link>
|
||||
<Link to="/shop" className="hover:text-spa-forest transition-colors">Shop</Link>
|
||||
<span>/</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>
|
||||
<span className="text-spa-forest">{typeof category?.name === 'string' ? category.name : (category?.name as any)?.[localStorage.getItem('app_lang') || 'en'] || (category?.name as any)?.en || '...'}</span>
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-spa-light">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-spa-forest">
|
||||
{loading ? (
|
||||
<div className="h-10 w-48 bg-spa-light animate-pulse rounded-lg" />
|
||||
<div className="h-10 w-48 bg-spa-stone/20 animate-pulse rounded-lg mx-auto md:mx-0" />
|
||||
) : (
|
||||
typeof category?.name === 'string' ? category.name : (category?.name as any)?.[i18n.language] || (category?.name as any)?.en || ''
|
||||
)}
|
||||
</h1>
|
||||
{!loading && category?._count && (
|
||||
<p className="text-spa-sage mt-2">
|
||||
<p className="text-spa-stone mt-4">
|
||||
{category._count.products} products available
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative w-48 h-48 md:w-72 md:h-72 shrink-0 rounded-3xl overflow-hidden shadow-lg border border-spa-stone/10">
|
||||
<img
|
||||
src={getCategoryImage(typeof category?.name === 'string' ? category.name : (category?.name as any)?.en || '')}
|
||||
className="absolute inset-0 w-full h-full object-cover"
|
||||
alt={typeof category?.name === 'string' ? category.name : (category?.name as any)?.[i18n.language] || (category?.name as any)?.en || ''}
|
||||
onError={(e) => { (e.target as HTMLImageElement).style.display = 'none' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Products Grid */}
|
||||
<div className="max-w-7xl mx-auto px-6 py-16 space-y-10">
|
||||
{/* Other Category Pills */}
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function HomePage() {
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-pop-bg">
|
||||
<Navbar categories={categories} />
|
||||
<Navbar categories={categories} isTransparent={true} />
|
||||
|
||||
{loading ? (
|
||||
<div className="min-h-[60vh] flex items-center justify-center">
|
||||
|
||||
Reference in new issue
Block a user