Initial commit of the Mado Motor public website.
This commit is contained in:
@@ -0,0 +1,369 @@
|
||||
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'
|
||||
|
||||
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} />
|
||||
}
|
||||
Reference in New Issue
Block a user