371 lines
14 KiB
TypeScript
371 lines
14 KiB
TypeScript
import { useEffect, useRef, useState } from 'react'
|
|
import { useParams } from 'react-router-dom'
|
|
import PrimaryButton from '../components/PrimaryButton'
|
|
import {
|
|
resolveLandingModel,
|
|
type LandingModel,
|
|
} from '../data/landingModels'
|
|
import { listPublicBlogs, type PublicBlog } from '../lib/api'
|
|
import { toFaDigits } from '../lib/faDigits'
|
|
import SeoHead from '../components/SeoHead'
|
|
import watermarkBg from '../assets/images/mado-230-bg.png'
|
|
import BlogCarousel from '../components/BlogCarousel'
|
|
import NotFoundPage from './NotFoundPage'
|
|
import './LandingModelPage.css'
|
|
|
|
function ColorCarousel({ images }: { images: string[] }) {
|
|
const [index, setIndex] = useState(0)
|
|
if (images.length === 0) return null
|
|
|
|
return (
|
|
<div className="landing-carousel">
|
|
<div className="landing-carousel__frame">
|
|
<img src={images[index]} alt="" />
|
|
</div>
|
|
<div className="landing-carousel__dots">
|
|
{images.map((_, i) => (
|
|
<button
|
|
key={i}
|
|
type="button"
|
|
className={i === index ? 'is-active' : ''}
|
|
aria-label={`رنگ ${i + 1}`}
|
|
onClick={() => setIndex(i)}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
function LandingContent({ model }: { model: LandingModel }) {
|
|
const [blogs, setBlogs] = useState<PublicBlog[]>([])
|
|
const rootRef = useRef<HTMLDivElement>(null)
|
|
const modelCode = model.label.replace('مادو ', '')
|
|
|
|
useEffect(() => {
|
|
void listPublicBlogs()
|
|
.then((data) => setBlogs(data))
|
|
.catch(() => setBlogs([]))
|
|
}, [model])
|
|
|
|
useEffect(() => {
|
|
const root = rootRef.current
|
|
if (!root) return
|
|
const nodes = [...root.querySelectorAll('.js-reveal')]
|
|
const io = new IntersectionObserver(
|
|
(entries) => {
|
|
for (const entry of entries) {
|
|
if (!entry.isIntersecting) continue
|
|
entry.target.classList.add('is-in')
|
|
io.unobserve(entry.target)
|
|
}
|
|
},
|
|
{ threshold: 0.16, rootMargin: '0px 0px -8% 0px' },
|
|
)
|
|
nodes.forEach((node) => io.observe(node))
|
|
return () => io.disconnect()
|
|
}, [model.id, blogs.length])
|
|
|
|
return (
|
|
<div className="landing" key={model.id} ref={rootRef}>
|
|
<SeoHead
|
|
title={`${model.titleFa} - موتور سکلیت برقی`}
|
|
description={model.description}
|
|
path={`/landing/${model.id}/${model.slug}`}
|
|
/>
|
|
<section className="landing__slider">
|
|
<div
|
|
className="landing__slider-bg"
|
|
style={{ backgroundImage: `url(${watermarkBg})` }}
|
|
aria-hidden="true"
|
|
/>
|
|
<div className="container landing__slider-inner">
|
|
<div className="landing__hero-title">
|
|
<h1>
|
|
<span className="landing__chip landing__chip--muted landing__chip--lg">
|
|
موتورسیکلت
|
|
</span>
|
|
<strong className="landing__chip landing__chip--xl text-gradient-1 text-shadow">
|
|
{model.label}
|
|
</strong>
|
|
</h1>
|
|
<PrimaryButton to="#learn-more" title="بــــــــــرو بریــــــــــــــــم" />
|
|
</div>
|
|
|
|
<div
|
|
className={`landing__hero-row ${model.heroExtras ? 'landing__hero-row--extras' : ''}`}
|
|
>
|
|
<div className="landing__stat landing__stat--speed">
|
|
<span className="landing__chip landing__chip--lg text-gradient-2">
|
|
حداکثر سرعت
|
|
</span>
|
|
<div className="landing__stat-line">
|
|
<strong className="landing__chip landing__chip--xl">
|
|
{toFaDigits(model.maxSpeed)}
|
|
</strong>
|
|
<span className="landing__chip landing__chip--lg">کیلومتــــر</span>
|
|
</div>
|
|
{model.heroExtras?.[0] ? (
|
|
<div className="landing__extra">
|
|
<img src={model.heroExtras[0].image} alt="" width={90} />
|
|
<strong className="landing__chip landing__chip--lg">
|
|
{model.heroExtras[0].title}
|
|
</strong>
|
|
<span className="landing__chip landing__chip--md en text-muted">
|
|
{model.heroExtras[0].subtitle}
|
|
</span>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
|
|
<div className="landing__stat landing__stat--range landing__stat--mobile">
|
|
<span className="landing__chip landing__chip--lg text-gradient-2">پیمایش تا</span>
|
|
<div className="landing__stat-line">
|
|
<strong className="landing__chip landing__chip--xl">
|
|
{toFaDigits(model.rangeValue)}
|
|
</strong>
|
|
<span className="landing__chip landing__chip--lg">کیلومتر</span>
|
|
</div>
|
|
<span className="landing__chip landing__chip--md">{model.rangeNote}</span>
|
|
</div>
|
|
|
|
<div className="landing__hero-image">
|
|
<img src={model.heroImage} alt={model.titleFa} />
|
|
</div>
|
|
|
|
<div className="landing__stat landing__stat--range landing__stat--desktop">
|
|
<span className="landing__chip landing__chip--lg text-gradient-2">پیمایش تا</span>
|
|
<div className="landing__stat-stack">
|
|
<div className="landing__stat-line">
|
|
<strong className="landing__chip landing__chip--xl">
|
|
{toFaDigits(model.rangeValue)}
|
|
</strong>
|
|
<span className="landing__chip landing__chip--lg">کیلومتر</span>
|
|
</div>
|
|
<span className="landing__chip landing__chip--md">{model.rangeNote}</span>
|
|
</div>
|
|
{model.heroExtras?.[1] ? (
|
|
<div className="landing__extra">
|
|
<img src={model.heroExtras[1].image} alt="" width={80} />
|
|
<strong className="landing__chip landing__chip--lg">
|
|
{model.heroExtras[1].title}
|
|
</strong>
|
|
<span className="landing__chip landing__chip--md en text-muted">
|
|
{model.heroExtras[1].subtitle}
|
|
</span>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="landing__scroll">
|
|
<img src={model.scrollIcon} alt="" width={25} height={25} />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="learn-more" className="landing__section js-reveal reveal">
|
|
<div className="container landing__intro">
|
|
<img src={model.introImage} alt="" />
|
|
<div>
|
|
<div className="landing__kicker">
|
|
<span className="landing__chip landing__chip--sm text-muted">مـــــــــادو</span>
|
|
<strong className="landing__chip landing__chip--md text-gradient-1 text-shadow">
|
|
{modelCode}
|
|
</strong>
|
|
</div>
|
|
<p className="landing__copy">{model.introText}</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
className="landing__band js-reveal reveal"
|
|
style={{ backgroundImage: `url(${model.bg1})` }}
|
|
>
|
|
<div className="container landing__band-inner">
|
|
<img className="landing__band-media" src={model.dashImage} alt="" />
|
|
<div className="landing__band-copy">
|
|
{model.featureBlocks?.length ? (
|
|
model.featureBlocks.map((block) => (
|
|
<div key={block.title} className="landing__feature">
|
|
<div className="landing__kicker">
|
|
<span className="landing__chip landing__chip--sm">{block.eyebrow}</span>
|
|
<strong className="landing__chip landing__chip--md text-gradient-2 text-shadow">
|
|
{block.title}
|
|
</strong>
|
|
</div>
|
|
<p className="landing__copy">{block.body}</p>
|
|
</div>
|
|
))
|
|
) : (
|
|
<>
|
|
<div className="landing__kicker">
|
|
<span className="landing__chip landing__chip--sm">کیلومتر</span>
|
|
<strong className="landing__chip landing__chip--md text-gradient-2 text-shadow">
|
|
دیجیتالی
|
|
</strong>
|
|
</div>
|
|
<p className="landing__copy">{model.dashText}</p>
|
|
</>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
className="landing__highlight js-reveal reveal"
|
|
style={{ backgroundImage: `url(${model.bg2})` }}
|
|
>
|
|
<div className="landing__highlight-inner">
|
|
<img
|
|
src={model.highlight.icon}
|
|
alt=""
|
|
width={model.highlight.iconWidth}
|
|
height={model.highlight.iconWidth}
|
|
/>
|
|
{model.id === 'mado-230' ? (
|
|
<>
|
|
<span className="landing__chip landing__chip--sm text-muted">قابلیت</span>
|
|
<strong className="landing__chip landing__chip--xl text-gradient-3 text-shadow">
|
|
فست شارژ
|
|
</strong>
|
|
<span className="landing__chip landing__chip--sm text-muted">به زودی...</span>
|
|
</>
|
|
) : (
|
|
<>
|
|
<strong className="landing__chip landing__chip--xl text-gradient-3 text-shadow">
|
|
{model.highlight.title}
|
|
</strong>
|
|
{model.highlight.body ? (
|
|
<p className="landing__copy landing__copy--center">{model.highlight.body}</p>
|
|
) : null}
|
|
</>
|
|
)}
|
|
</div>
|
|
</section>
|
|
|
|
<section className="landing__section js-reveal reveal">
|
|
<div className="container landing__colors">
|
|
<strong className="landing__chip landing__chip--md text-gradient-4 text-shadow">
|
|
{model.colorTitle}
|
|
</strong>
|
|
{model.colorSubtitles.map((line) => (
|
|
<p key={line} className="landing__copy landing__copy--center">
|
|
{line}
|
|
</p>
|
|
))}
|
|
<ColorCarousel images={model.colors} />
|
|
</div>
|
|
</section>
|
|
|
|
<section className="landing__split js-reveal reveal reveal--split">
|
|
<img src={model.batteryImage} alt="" />
|
|
<div>
|
|
<div className="landing__kicker column">
|
|
<span className="landing__chip landing__chip--sm text-muted">باطری</span>
|
|
<strong className="landing__chip landing__chip--md text-gradient-1 text-shadow">
|
|
لیتیومی نسل جدید
|
|
</strong>
|
|
</div>
|
|
<ul>
|
|
{model.batteryItems.map((item) => (
|
|
<li key={item}>{item}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
className="landing__band js-reveal reveal"
|
|
style={{ backgroundImage: `url(${model.bg1})` }}
|
|
>
|
|
<div className="container landing__band-inner landing__band-inner--wide">
|
|
<img src={model.partsImage} alt="" />
|
|
<p className="landing__copy">
|
|
دسترسی آسان به تمامی قطعات بواسطه تولید 80 قطعه از 100 قطعه اصلی در داخل کشور قیمت مناسب
|
|
خدمات و قطعات موتور سیکلت
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section className="landing__split js-reveal reveal reveal--split">
|
|
<img src={model.ecoImage} alt="" />
|
|
<div>
|
|
<div className="landing__kicker column">
|
|
<span className="landing__chip landing__chip--sm text-muted">مادوموتور</span>
|
|
<strong className="landing__chip landing__chip--md text-gradient-5">
|
|
دوست دار محیط زیست
|
|
</strong>
|
|
</div>
|
|
<p className="landing__copy">
|
|
با موتور سیکت های برقی مادو موتور با کاهش آلایندگی هوا به سلامت یکدیگر کمک کنیم
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
className="landing__guarantee js-reveal reveal"
|
|
style={{ backgroundImage: `url(${model.guaranteeTypo})` }}
|
|
>
|
|
<div className="container landing__guarantee-inner">
|
|
<img src={model.guaranteeImage} alt="گارانتی" />
|
|
<div>
|
|
<div className="landing__kicker column">
|
|
<span className="landing__chip landing__chip--sm text-muted">گارانتی</span>
|
|
<strong className="landing__chip landing__chip--md text-gradient-1 text-shadow">
|
|
بلند مدت و خدمات پس از فروش
|
|
</strong>
|
|
</div>
|
|
<ul>
|
|
{model.guaranteeItems.map((item) => (
|
|
<li key={item}>{item}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
className="landing__band landing__band--price js-reveal reveal"
|
|
style={{ backgroundImage: `url(${model.bg1})` }}
|
|
>
|
|
<div className="container landing__band-inner">
|
|
<img src={model.priceImage} alt="" />
|
|
<div>
|
|
<div className="landing__kicker column">
|
|
<span className="landing__chip landing__chip--sm text-muted">قیمت</span>
|
|
<strong className="landing__chip landing__chip--md text-gradient-1">
|
|
و شرایط مناسب
|
|
</strong>
|
|
</div>
|
|
<ul>
|
|
{model.priceItems.map((item) => (
|
|
<li key={item}>{item}</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{blogs.length > 0 ? (
|
|
<section className="landing__section js-reveal reveal">
|
|
<div className="container landing__blogs">
|
|
<strong className="landing__chip landing__chip--lg text-gradient-2">
|
|
آخرین مقالات
|
|
</strong>
|
|
<BlogCarousel blogs={blogs} />
|
|
</div>
|
|
</section>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default function LandingModelPage() {
|
|
const { model } = useParams()
|
|
const landing = resolveLandingModel(model)
|
|
if (!landing) return <NotFoundPage />
|
|
return <LandingContent model={landing} />
|
|
}
|