This commit is contained in:
Your Name committed 2026-07-10 20:27:26 -04:00
1 parent fc252e46cc
commit ad637fd8b4
27 files changed
+764 -25

No files matched your search

@@ -2,7 +2,7 @@
import Image from 'next/image'
import Link from '../LocalizedLink'
import { Clock, Mail, MapPin, MessageCircle, Send, ChevronLeft, ChevronRight } from 'lucide-react'
import { Clock, Mail, MapPin, MessageCircle, Send, ChevronLeft, ChevronRight, Leaf, Droplet, Sparkles, Gift, CheckCircle, ShieldCheck, Heart } from 'lucide-react'
import { useState, useRef } from 'react'
import type { Category, PageSection, Product } from '@homaroma/types'
import { getCategoryImage } from '@homaroma/types'
@@ -152,6 +152,140 @@ function HeroSection({ payload, lang }: { payload: Record<string, unknown>, lang
)
}
const ICON_MAP: Record<string, React.ElementType> = {
leaf: Leaf,
droplet: Droplet,
sparkles: Sparkles,
gift: Gift,
check: CheckCircle,
shield: ShieldCheck,
heart: Heart,
}
function UspBarSection({ payload, lang }: { payload: Record<string, unknown>, lang: string }) {
const i18nLang = useLang()
const resolvedLang = lang === 'all' ? i18nLang : lang
const items = Array.isArray(payload.items) ? payload.items : []
const backgroundColor = typeof payload.backgroundColor === 'string' ? payload.backgroundColor : 'var(--surface-page)'
if (items.length === 0) return null
return (
<section className="w-full py-8 border-t border-b border-line-primary/10" style={{ backgroundColor }}>
<div className="ui-section-shell">
<div className={`grid grid-cols-1 sm:grid-cols-2 md:grid-cols-${Math.min(items.length, 4)} gap-6 divide-y sm:divide-y-0 sm:divide-x divide-line-primary/20`}>
{items.slice(0, 4).map((item: any, idx: number) => {
const Icon = ICON_MAP[item.icon] || CheckCircle
return (
<div key={idx} className="flex items-start gap-4 px-4 pt-4 sm:pt-0 first:pt-0">
<div className="flex-shrink-0 text-brand-primary">
<Icon className="w-8 h-8" strokeWidth={1.5} />
</div>
<div>
<h3 className="text-sm md:text-base font-semibold text-text-main">
{localizedFromRecord(item, 'title', resolvedLang)}
</h3>
{item.subtitle && (
<p className="text-xs md:text-sm text-muted-text mt-1">
{localizedFromRecord(item, 'subtitle', resolvedLang)}
</p>
)}
</div>
</div>
)
})}
</div>
</div>
</section>
)
}
function HeroV2Section({ payload, lang }: { payload: Record<string, unknown>, lang: string }) {
const i18nLang = useLang()
const resolvedLang = lang === 'all' ? i18nLang : lang
const tagline = localizedFromRecord(payload, 'tagline', resolvedLang)
const title = localizedFromRecord(payload, 'title', resolvedLang)
const description = localizedFromRecord(payload, 'description', resolvedLang)
const primaryBtnText = localizedFromRecord(payload, 'primaryButtonText', resolvedLang)
const primaryBtnLink = typeof payload.primaryButtonLink === 'string' ? payload.primaryButtonLink : undefined
const secondaryBtnText = localizedFromRecord(payload, 'secondaryButtonText', resolvedLang)
const secondaryBtnLink = typeof payload.secondaryButtonLink === 'string' ? payload.secondaryButtonLink : undefined
const imageUrl = typeof payload.imageUrl === 'string' ? payload.imageUrl : '/images/hero.png'
const align = typeof payload.contentAlignment === 'string' ? payload.contentAlignment : 'left'
const showOrnament = typeof payload.showOrnament === 'boolean' ? payload.showOrnament : true
// Alignment classes
const alignClass = align === 'center' ? 'text-center items-center mx-auto' : align === 'right' ? 'text-right items-end ml-auto' : 'text-left items-start'
return (
<section className="relative w-full h-[80vh] min-h-[600px] bg-surface-page flex items-center overflow-hidden">
<div className="absolute inset-0 w-full h-full">
<Image
src={imageUrl}
alt={title || 'Hero'}
fill
unoptimized
className="object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-r from-surface-page/90 via-surface-page/50 to-transparent pointer-events-none" />
</div>
<div className="relative z-10 w-full max-w-7xl mx-auto px-6 md:px-12">
<div className={`max-w-xl flex flex-col gap-6 ${alignClass}`}>
{tagline && (
<div className="flex items-center gap-4">
{showOrnament && align !== 'right' && <div className="w-8 h-[1px] bg-brand-primary" />}
<span className="text-sm font-semibold tracking-[0.2em] uppercase text-brand-primary">
{tagline}
</span>
{showOrnament && align !== 'left' && <div className="w-8 h-[1px] bg-brand-primary" />}
</div>
)}
<h1 className="text-4xl md:text-5xl lg:text-6xl font-serif text-text-main leading-tight">
{renderAccentTitle(title || '', 'text-brand-primary')}
</h1>
{showOrnament && (
<div className="flex justify-center items-center text-brand-primary my-2">
<Sparkles className="w-5 h-5" />
</div>
)}
{description && (
<p className="text-base md:text-lg text-text-main/80 leading-relaxed max-w-md">
{description}
</p>
)}
<div className={`flex flex-col sm:flex-row items-center gap-4 mt-4 w-full sm:w-auto ${align === 'center' ? 'justify-center' : align === 'right' ? 'justify-end' : 'justify-start'}`}>
{primaryBtnText && primaryBtnLink && (
<Link
href={primaryBtnLink}
className="inline-flex items-center justify-center gap-2 px-8 py-3.5 bg-[#A18868] text-white font-medium rounded-md hover:bg-[#8B7355] transition-colors w-full sm:w-auto"
>
{primaryBtnText}
<ChevronRight className="w-4 h-4" />
</Link>
)}
{secondaryBtnText && secondaryBtnLink && (
<Link
href={secondaryBtnLink}
className="inline-flex items-center justify-center gap-2 px-8 py-3.5 bg-transparent border border-[#A18868] text-[#A18868] font-medium rounded-md hover:bg-[#A18868]/5 transition-colors w-full sm:w-auto"
>
{secondaryBtnText}
<ChevronRight className="w-4 h-4" />
</Link>
)}
</div>
</div>
</div>
</section>
)
}
function CategoriesSection({
payload,
categories,
@@ -888,6 +1022,18 @@ export default function DynamicPageRenderer({ sections, categories, lang: langPr
<HeroSection payload={payload} lang={lang} />
</ScrollReveal>
)
case 'HERO_V2':
return (
<ScrollReveal key={section.id}>
<HeroV2Section payload={payload} lang={lang} />
</ScrollReveal>
)
case 'USP_BAR':
return (
<ScrollReveal key={section.id}>
<UspBarSection payload={payload} lang={lang} />
</ScrollReveal>
)
case 'CATEGORIES':
return (
<ScrollReveal key={section.id}>