feat(cms): add Cinematic Story Scroll and Apple Showcase blocks to Page Builder
This commit is contained in:
1 parent
25cbe3549e
commit
8d9f2fc587
10 files changed
+971
-8
No files matched your search
@@ -9,6 +9,9 @@ interface Props {
|
||||
product: Product
|
||||
heroImage?: string
|
||||
lang?: string
|
||||
endCtaTitle?: string
|
||||
endCtaButtonText?: string
|
||||
endCtaLink?: string
|
||||
}
|
||||
|
||||
interface HighlightCard {
|
||||
@@ -184,7 +187,14 @@ const DEEP_FEATURES: DeepFeature[] = [
|
||||
},
|
||||
]
|
||||
|
||||
export default function AppleWatchShowcase({ product, heroImage, lang = 'th' }: Props) {
|
||||
export default function AppleWatchShowcase({
|
||||
product,
|
||||
heroImage,
|
||||
lang = 'th',
|
||||
endCtaTitle,
|
||||
endCtaButtonText,
|
||||
endCtaLink,
|
||||
}: Props) {
|
||||
const productName = resolveLocalized(product.name, lang)
|
||||
const mainImage = heroImage || product.images?.[0]?.url || '/placeholder.jpg'
|
||||
|
||||
@@ -231,9 +241,15 @@ export default function AppleWatchShowcase({ product, heroImage, lang = 'th' }:
|
||||
const currentHighlight = HIGHLIGHTS[activeHighlight]
|
||||
|
||||
const scrollToPurchase = () => {
|
||||
if (endCtaLink) {
|
||||
window.location.href = endCtaLink
|
||||
return
|
||||
}
|
||||
const el = document.getElementById('product-purchase-top')
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
} else {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -619,8 +635,7 @@ export default function AppleWatchShowcase({ product, heroImage, lang = 'th' }:
|
||||
<div className="max-w-3xl mx-auto space-y-6">
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-slate-500">READY TO UPGRADE</p>
|
||||
<h2 className="text-4xl sm:text-6xl font-black tracking-tight text-white">
|
||||
อัปเกรดระบบเบรก<br />
|
||||
สู่มาตรฐานระดับโลก
|
||||
{endCtaTitle ? endCtaTitle : <>อัปเกรดระบบเบรก<br />สู่มาตรฐานระดับโลก</>}
|
||||
</h2>
|
||||
<p className="text-base sm:text-lg text-slate-400 leading-relaxed">
|
||||
พร้อมรับประกันคุณภาพและความเข้ากันได้กับรถของคุณ สั่งซื้อวันนี้เพื่อสัมผัสความมั่นใจในทุกระยะหยุด
|
||||
@@ -632,7 +647,7 @@ export default function AppleWatchShowcase({ product, heroImage, lang = 'th' }:
|
||||
onClick={scrollToPurchase}
|
||||
className="inline-flex items-center justify-center rounded-2xl bg-white px-10 py-4 text-base font-black text-slate-950 hover:bg-slate-200 active:scale-95 transition-all shadow-2xl shadow-white/20"
|
||||
>
|
||||
เลือกซื้อรุ่นของคุณ (Select Model) ↑
|
||||
{endCtaButtonText || 'เลือกซื้อรุ่นของคุณ (Select Model) ↑'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,6 +24,7 @@ interface Props {
|
||||
lang: string
|
||||
endCtaTitle?: string
|
||||
endCtaButtonText?: string
|
||||
endCtaLink?: string
|
||||
storyTheme?: 'dark' | 'light' | string
|
||||
}
|
||||
|
||||
@@ -89,6 +90,7 @@ export default function CinematicStoryScroll({
|
||||
lang,
|
||||
endCtaTitle,
|
||||
endCtaButtonText,
|
||||
endCtaLink,
|
||||
storyTheme = 'light',
|
||||
}: Props) {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null)
|
||||
@@ -212,6 +214,10 @@ export default function CinematicStoryScroll({
|
||||
}, [activeIndex, activeScenes.length, scrollToScene])
|
||||
|
||||
const scrollToPurchase = () => {
|
||||
if (endCtaLink) {
|
||||
window.location.href = endCtaLink
|
||||
return
|
||||
}
|
||||
const el = document.getElementById('product-purchase-top')
|
||||
if (el) {
|
||||
el.scrollIntoView({ behavior: 'smooth' })
|
||||
|
||||
@@ -16,6 +16,8 @@ import { Swiper, SwiperSlide } from 'swiper/react'
|
||||
import { Autoplay, Pagination, Navigation } from 'swiper/modules'
|
||||
import ScrollReveal from '../ScrollReveal'
|
||||
import PageDivider from '../PageDivider'
|
||||
import CinematicStoryScroll from '../CinematicStoryScroll'
|
||||
import AppleWatchShowcase from '../AppleWatchShowcase'
|
||||
import 'swiper/css'
|
||||
import 'swiper/css/pagination'
|
||||
import 'swiper/css/navigation'
|
||||
@@ -2600,6 +2602,11 @@ export default function DynamicPageRenderer({ sections, categories, lang: langPr
|
||||
'HERO_SPLIT_MINIMAL',
|
||||
'HERO_VIDEO_MINIMAL',
|
||||
'HERO_CAROUSEL',
|
||||
'CINEMATIC_STORY_SCROLL',
|
||||
'STORY_SCROLL',
|
||||
'APPLE_SHOWCASE',
|
||||
'APPLE_KEYNOTE_SHOWCASE',
|
||||
'APPLE_WATCH_TOUR',
|
||||
]), [])
|
||||
|
||||
const firstActiveSection = sections.find(s => s.isActive && s.type.toUpperCase() !== 'ANNOUNCEMENT_BAR' && s.type.toUpperCase() !== 'ANNOUNCEMENT_BAR_MINIMAL')
|
||||
@@ -2831,6 +2838,96 @@ export default function DynamicPageRenderer({ sections, categories, lang: langPr
|
||||
</ScrollReveal>
|
||||
)
|
||||
}
|
||||
|
||||
case 'CINEMATIC_STORY_SCROLL':
|
||||
case 'STORY_SCROLL': {
|
||||
const rawTheme = payload.storyTheme as string | undefined
|
||||
const storyTheme = rawTheme === 'dark' ? 'dark' : 'light'
|
||||
const storyHeroImage = (payload.storyHeroImage as string) || (payload.heroImage as string) || undefined
|
||||
const storyScenes = Array.isArray(payload.storyScenes) ? (payload.storyScenes as any[]) : undefined
|
||||
const endCtaTitle = (payload.endCtaTitle as string) || undefined
|
||||
const endCtaButtonText = (payload.endCtaButtonText as string) || undefined
|
||||
const endCtaLink = (payload.endCtaLink as string) || undefined
|
||||
|
||||
const targetProductId = Number(payload.productId) || 0
|
||||
const foundProduct = targetProductId
|
||||
? (categories.flatMap(c => (c as any).products || []) as Product[]).find(p => p.id === targetProductId)
|
||||
: undefined
|
||||
|
||||
const mockProduct: Product = foundProduct || {
|
||||
id: targetProductId || 1,
|
||||
name: { en: 'Product Story', th: 'การนำเสนอผลิตภัณฑ์' },
|
||||
description: { en: 'High Performance Story', th: 'การขับขี่สมรรถนะสูง' },
|
||||
details: null,
|
||||
price: 0,
|
||||
brandId: 1,
|
||||
categoryId: 1,
|
||||
images: storyHeroImage ? [{ id: 1, url: storyHeroImage, order: 0 }] : [],
|
||||
variants: [],
|
||||
options: [
|
||||
{
|
||||
id: 1,
|
||||
name: { en: 'Model', th: 'รุ่น' },
|
||||
values: ['NVX155', 'LC135', 'Vario125', 'Vario150', 'PCX150', 'Beat110', 'Wave110-I', 'R15'] as any
|
||||
}
|
||||
] as any,
|
||||
category: { id: 1, name: 'Braking' } as any,
|
||||
brand: { id: 1, name: 'Brand Story' } as any,
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={section.id} className="w-full">
|
||||
<CinematicStoryScroll
|
||||
product={mockProduct}
|
||||
heroImage={storyHeroImage}
|
||||
scenes={storyScenes}
|
||||
lang={lang}
|
||||
endCtaTitle={endCtaTitle}
|
||||
endCtaButtonText={endCtaButtonText}
|
||||
endCtaLink={endCtaLink}
|
||||
storyTheme={storyTheme}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
case 'APPLE_SHOWCASE':
|
||||
case 'APPLE_KEYNOTE_SHOWCASE':
|
||||
case 'APPLE_WATCH_TOUR': {
|
||||
const storyHeroImage = (payload.heroImage as string) || (payload.storyHeroImage as string) || undefined
|
||||
const targetProductId = Number(payload.productId) || 0
|
||||
const foundProduct = targetProductId
|
||||
? (categories.flatMap(c => (c as any).products || []) as Product[]).find(p => p.id === targetProductId)
|
||||
: undefined
|
||||
|
||||
const mockProduct: Product = foundProduct || {
|
||||
id: targetProductId || 1,
|
||||
name: { en: 'Keynote Showcase', th: 'การนำเสนอผลิตภัณฑ์' },
|
||||
description: { en: 'Next-Gen Performance', th: 'นวัตกรรมมาตรฐานระดับโลก' },
|
||||
details: null,
|
||||
price: 0,
|
||||
brandId: 1,
|
||||
categoryId: 1,
|
||||
images: storyHeroImage ? [{ id: 1, url: storyHeroImage, order: 0 }] : [],
|
||||
variants: [],
|
||||
category: { id: 1, name: 'Technology' } as any,
|
||||
brand: { id: 1, name: 'Keynote' } as any,
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={section.id} className="w-full">
|
||||
<AppleWatchShowcase
|
||||
product={mockProduct}
|
||||
heroImage={storyHeroImage}
|
||||
lang={lang}
|
||||
endCtaTitle={payload.endCtaTitle as string | undefined}
|
||||
endCtaButtonText={payload.endCtaButtonText as string | undefined}
|
||||
endCtaLink={payload.endCtaLink as string | undefined}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
default:
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.warn(`[DynamicPageRenderer] Unsupported section type: ${section.type}`)
|
||||
|
||||
Reference in new issue
Block a user