Files

880 lines
29 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import {
ArrowLeft,
ArrowUpLeft,
BriefcaseBusiness,
Camera,
ChevronLeft,
ChevronRight,
CircleUserRound,
FlaskConical,
Gauge,
Globe2,
Link as LinkIcon,
Mail,
MapPin,
Menu,
MessageCircle,
Phone,
Search,
Settings,
ShieldCheck,
SlidersHorizontal,
Sparkles,
X,
} from "lucide-react";
import NextLink from "next/link";
import type { FormEvent } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import {
cssAspectRatio,
fetchHomeStaticImageSlots,
fetchLatestProducts,
productPath,
type Product,
type StaticImageSlot,
} from "./lib/meshkee";
import HeaderNavigation from "./components/HeaderNavigation";
const assetBase = "/assets";
type HeroSlide = {
image: string;
eyebrow: string;
title: string;
text: string;
linkUrl?: string;
aspectRatio?: string;
};
const heroSlides: HeroSlide[] = [
{
image: `${assetBase}/i8SsUDDMLv.jpg`,
eyebrow: "WIRE & CABLE MACHINERY",
title: "تجربه‌ای مطمئن در تأمین ماشین‌آلات صنعتی",
text: "راهکارهای تخصصی برای خطوط تولید سیم و کابل؛ از انتخاب و خرید تا نصب و راه‌اندازی.",
},
{
image: `${assetBase}/s9zJRbKrhE.jpg`,
eyebrow: "ENGINEERING THE FUTURE",
title: "مهندسی دقیق، عملکرد ماندگار",
text: "دسترسی به برندهای معتبر جهانی و مشاوره فنی متناسب با نیاز خط تولید شما.",
},
{
image: `${assetBase}/ZLxH2tZ4dR.jpg`,
eyebrow: "SAMAN ANDISH CO.",
title: "یک مسیر کوتاه‌تر تا خط تولید بهتر",
text: "ماشین‌آلات نو و کارکرده، مواد اولیه و خدمات مهندسی در یک مجموعه تخصصی.",
},
];
const categories = [
{
title: "ماشین‌آلات",
en: "MACHINERY",
image: `${assetBase}/MDJVsqMgHe.jpg`,
count: "+۱۲۰ دستگاه",
},
{
title: "خدمات فنی و مهندسی",
en: "TECHNICAL SERVICES",
image: `${assetBase}/68atRerNly.jpg`,
count: "از طراحی تا اجرا",
},
{
title: "مواد اولیه",
en: "RAW MATERIALS",
image: `${assetBase}/yxEFuyUDrT.jpg`,
count: "تأمین تخصصی",
},
];
type DisplayProduct = {
id: string;
title: string;
en: string;
category: string;
image: string;
code: string;
href: string;
};
const fallbackProducts: DisplayProduct[] = [
{
id: "fallback-1",
title: "دستگاه کوادینگ ۶۰۰ میلی‌متر",
en: "600mm Quadding Machine | QUA-21 | LESMO",
category: "دستگاه تابنده",
image: `${assetBase}/product-quadding.jpg`,
code: "LESMO / QUA-21",
href: "#products",
},
{
id: "fallback-2",
title: "بریدر ۱۶ کریر",
en: "16 Carriers Braider | 16N4 | SPIRKA",
category: "دستگاه بافنده",
image: `${assetBase}/product-braider.jpg`,
code: "SPIRKA / 16N4",
href: "#products",
},
{
id: "fallback-3",
title: "خط کشش ۰.۵–۰.۱۵ میلی‌متر",
en: "0.15–0.5 SQmm Drawing Line | TRB3 | SAMP",
category: "دستگاه کشش",
image: `${assetBase}/product-drawing-line.jpg`,
code: "SAMP / TRB3",
href: "#products",
},
{
id: "fallback-4",
title: "پی‌آف ۶۰۰ میلی‌متر",
en: "600mm Pay Off | TAP D-0800 | KDM",
category: "دستگاه پی‌آف",
image: `${assetBase}/product-payoff.jpg`,
code: "KDM / TAP D-0800",
href: "#products",
},
];
function toDisplayProduct(product: Product, index: number): DisplayProduct {
const fallback = fallbackProducts[index % fallbackProducts.length];
const brand =
typeof product.brand === "string"
? product.brand
: product.brand?.nameFa || product.brand?.name;
return {
id: product.id,
title: product.nameFa || product.title,
en: product.title,
category:
product.categoryNameFa || product.categoryName || "ماشین‌آلات صنعتی",
image:
product.thumbnailUrl ||
product.thumbnail ||
product.image ||
fallback.image,
code: brand || product.categoryName || "SAMAN ANDISH",
href: productPath(product),
};
}
const news = [
{
title: "شرکت مهندسی، مشاوره و بازرگانی HService",
date: "۰۸ خرداد ۱۴۰۲",
image: `${assetBase}/news-hservice.jpg`,
tag: "همکاری تجاری",
},
{
title: "نمایشگاه‌های تجاری پیشرو Wire و Tube",
date: "۲۴ اردیبهشت ۱۴۰۲",
image: `${assetBase}/news-wire-tube.jpg`,
tag: "رویداد",
},
{
title: "انرژی پاک و بازار گواهی انرژی تجدیدپذیر",
date: "۱۲ اردیبهشت ۱۴۰۲",
image: `${assetBase}/news-green-energy.jpg`,
tag: "دانش فنی",
},
];
const customerLogos = [
"JkHpH80NQA.png",
"StBGKS3FHm.png",
"GAQiN0WIK5.png",
"lRkE9W3Pi0.png",
"mTdHxPHztj.png",
"JcGMFJ8Tv5.png",
"WIvmqxoUPv.png",
"XhaGH9MU5o.png",
"PypItIBoI0.png",
"0Y6asKSTsI.png",
];
function SectionTitle({
eyebrow,
title,
light = false,
}: {
eyebrow: string;
title: string;
light?: boolean;
}) {
return (
<div className={`section-title ${light ? "section-title--light" : ""}`}>
<span>{eyebrow}</span>
<h2>{title}</h2>
<i />
</div>
);
}
export default function Home() {
const [mobileOpen, setMobileOpen] = useState(false);
const [activeSlide, setActiveSlide] = useState(0);
const [query, setQuery] = useState("");
const [searchOpen, setSearchOpen] = useState(false);
const [staticImageSlots, setStaticImageSlots] = useState<StaticImageSlot[]>([]);
const [displayProducts, setDisplayProducts] =
useState<DisplayProduct[]>(fallbackProducts);
const searchInput = useRef<HTMLInputElement>(null);
const slotsByKey = useMemo(
() => new Map(staticImageSlots.map((slot) => [slot.key, slot])),
[staticImageSlots],
);
const activeHeroSlides = useMemo<HeroSlide[]>(() => {
const slot = slotsByKey.get("slider");
if (!slot?.images.length) return heroSlides;
return slot.images.map((image, index) => {
const fallback = heroSlides[index % heroSlides.length];
return {
image: image.url || fallback.image,
eyebrow: image.titleEn || fallback.eyebrow,
title: image.titleFa || fallback.title,
text: image.subtext || fallback.text,
linkUrl: image.linkUrl || undefined,
aspectRatio: cssAspectRatio(slot.aspectRatio),
};
});
}, [slotsByKey]);
const activeCategories = useMemo(() => {
const slot = slotsByKey.get("three-categories");
return categories.map((fallback, index) => {
const image = slot?.images[index];
if (!image) {
return {
...fallback,
linkUrl: undefined,
aspectRatio: undefined,
};
}
return {
...fallback,
image: image.url || fallback.image,
title: image.titleFa || fallback.title,
en: image.titleEn || fallback.en,
count: image.subtext || fallback.count,
linkUrl: image.linkUrl || undefined,
aspectRatio: cssAspectRatio(slot?.aspectRatio),
};
});
}, [slotsByKey]);
const aboutImages = slotsByKey.get("about-us-images");
const aboutMainImage = aboutImages?.images[0];
const aboutSecondaryImage = aboutImages?.images[1];
const activeHeroIndex = activeSlide % activeHeroSlides.length;
const activeHero = activeHeroSlides[activeHeroIndex];
useEffect(() => {
const controller = new AbortController();
fetchHomeStaticImageSlots(controller.signal)
.then(setStaticImageSlots)
.catch(() => {
// Keep the local image fallbacks when the API is unavailable.
});
fetchLatestProducts(4, controller.signal)
.then((items) => {
if (items.length) setDisplayProducts(items.map(toDisplayProduct));
})
.catch(() => {
// Keep the local product fallbacks when the API is unavailable.
});
return () => controller.abort();
}, []);
useEffect(() => {
if (activeHeroSlides.length <= 1) return;
const timer = window.setInterval(
() =>
setActiveSlide((current) => (current + 1) % activeHeroSlides.length),
6500,
);
return () => window.clearInterval(timer);
}, [activeHeroSlides.length]);
const results = useMemo(() => {
const normalized = query.trim().toLowerCase();
if (!normalized) return displayProducts.slice(0, 3);
return displayProducts.filter((product) =>
[product.title, product.en, product.category, product.code]
.join(" ")
.toLowerCase()
.includes(normalized),
);
}, [displayProducts, query]);
const focusSearch = () => {
document.getElementById("smart-search")?.scrollIntoView({
behavior: "smooth",
block: "center",
});
window.setTimeout(() => {
searchInput.current?.focus();
setSearchOpen(true);
}, 500);
};
const submitSearch = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
setSearchOpen(true);
};
return (
<main>
<div className="topbar">
<div className="container topbar__inner">
<p>
<Sparkles size={14} />
شریک تخصصی صنعت سیم و کابل ایران
</p>
<div>
<a href="mailto:info@samanandish.com">
<Mail size={14} />
info@samanandish.com
</a>
<span />
<a href="tel:+982188530256" dir="ltr">
<Phone size={14} />
+98 21 8853 0256
</a>
</div>
</div>
</div>
<header className="header">
<div className="container header__inner">
<a className="logo" href="#" aria-label="سامان اندیش">
<img src={`${assetBase}/KN83Drb6oI.png`} alt="سامان اندیش" />
</a>
<HeaderNavigation
active="home"
mobileOpen={mobileOpen}
onNavigate={() => setMobileOpen(false)}
/>
<div className="header__actions">
<button className="icon-button" onClick={focusSearch} aria-label="جستجو">
<Search size={22} />
</button>
<button className="icon-button user-button" aria-label="حساب کاربری">
<CircleUserRound size={22} />
</button>
<a className="header__cta" href="/contact-us">
درخواست مشاوره
<ArrowUpLeft size={17} />
</a>
<button
className="mobile-toggle"
onClick={() => setMobileOpen((open) => !open)}
aria-label="منو"
>
{mobileOpen ? <X /> : <Menu />}
</button>
</div>
</div>
</header>
<section className="hero">
{activeHeroSlides.map((slide, index) => (
<div
className={`hero__slide ${index === activeHeroIndex ? "is-active" : ""}`}
key={`${slide.image}-${index}`}
style={{ aspectRatio: slide.aspectRatio }}
>
{slide.linkUrl ? (
<a
className="hero__slide-link"
href={slide.linkUrl}
aria-label={slide.title}
>
<img src={slide.image} alt={slide.title} />
</a>
) : (
<img src={slide.image} alt={slide.title} />
)}
</div>
))}
<div className="hero__overlay" />
<div className="hero__pattern" />
<div className="container hero__content">
<div className="hero__copy">
<p className="eyebrow">{activeHero.eyebrow}</p>
<h1>{activeHero.title}</h1>
<p className="hero__description">{activeHero.text}</p>
<div className="hero__buttons">
<NextLink className="button button--red" href="/products">
مشاهده محصولات
<ArrowLeft size={19} />
</NextLink>
<a className="button button--glass" href="/about-us">
درباره سامان اندیش
</a>
</div>
</div>
<div className="hero__stats" dir="ltr">
<div>
<strong>25+</strong>
<span>YEARS EXPERIENCE</span>
</div>
<i />
<div>
<strong>120+</strong>
<span>INDUSTRIAL MACHINES</span>
</div>
</div>
</div>
<div className="hero__controls">
<button
onClick={() =>
setActiveSlide(
(activeHeroIndex - 1 + activeHeroSlides.length) %
activeHeroSlides.length,
)
}
aria-label="اسلاید قبلی"
>
<ChevronRight />
</button>
<div>
{activeHeroSlides.map((slide, index) => (
<button
className={index === activeHeroIndex ? "active" : ""}
key={`${slide.image}-${index}`}
onClick={() => setActiveSlide(index)}
aria-label={`اسلاید ${index + 1}`}
/>
))}
</div>
<button
onClick={() =>
setActiveSlide((activeHeroIndex + 1) % activeHeroSlides.length)
}
aria-label="اسلاید بعدی"
>
<ChevronLeft />
</button>
</div>
</section>
<section className="search-panel" id="smart-search">
<div className="container">
<div className="search-card">
<div className="search-card__title">
<span>
<Search size={26} />
</span>
<div>
<strong>دنبال چه دستگاهی هستید؟</strong>
<small>نام، مدل، برند یا دسته‌بندی را وارد کنید</small>
</div>
</div>
<form onSubmit={submitSearch}>
<input
ref={searchInput}
type="search"
value={query}
onChange={(event) => {
setQuery(event.target.value);
setSearchOpen(true);
}}
onFocus={() => setSearchOpen(true)}
placeholder="مثلاً اکسترودر، دستگاه کشش یا Maillefer..."
/>
<button type="submit">
جستجوی محصولات
<Search size={18} />
</button>
</form>
<div className="quick-search">
<span>جستجوی سریع:</span>
{["اکسترودر", "دستگاه کشش", "تیک‌آپ", "پی‌آف"].map((item) => (
<button
key={item}
onClick={() => {
setQuery(item);
setSearchOpen(true);
}}
>
{item}
</button>
))}
</div>
{searchOpen && (
<div className="search-results">
<div className="search-results__head">
<span>{results.length} نتیجه پیشنهادی</span>
<button onClick={() => setSearchOpen(false)} aria-label="بستن">
<X size={17} />
</button>
</div>
{results.length ? (
results.map((product) => (
<NextLink href={product.href} key={product.id}>
<img src={product.image} alt={product.title} />
<div>
<strong>{product.title}</strong>
<small dir="ltr">{product.en}</small>
</div>
<span>{product.category}</span>
<ChevronLeft size={18} />
</NextLink>
))
) : (
<div className="empty-result">
دستگاهی با این مشخصات پیدا نشد؛ برای راهنمایی با ما تماس بگیرید.
</div>
)}
</div>
)}
</div>
</div>
</section>
<section className="categories" id="services">
<div className="container">
<SectionTitle
eyebrow="OUR EXPERTISE"
title="سه مسیر، یک راهکار کامل صنعتی"
/>
<div className="category-grid">
{activeCategories.map((category, index) => (
<a
className="category-card"
href={category.linkUrl || "#"}
key={`${category.title}-${index}`}
style={
category.aspectRatio
? { aspectRatio: category.aspectRatio, height: "auto" }
: undefined
}
>
<img src={category.image} alt={category.title} />
<div className="category-card__shade" />
<span className="category-card__number">0{index + 1}</span>
<div className="category-card__content">
<small>{category.en}</small>
<h3>{category.title}</h3>
<p>{category.count}</p>
</div>
<i>
<ArrowUpLeft />
</i>
</a>
))}
</div>
</div>
</section>
<section className="products section-gray" id="products">
<div className="container">
<div className="section-heading-row">
<SectionTitle eyebrow="LATEST MACHINERY" title="جدیدترین ماشین‌آلات" />
<NextLink className="text-link" href="/products">
مشاهده همه محصولات
<ArrowLeft size={18} />
</NextLink>
</div>
<div className="product-grid">
{displayProducts.map((product) => (
<article className="product-card" key={product.id}>
<NextLink className="product-card__image" href={product.href}>
<img src={product.image} alt={product.title} />
<span>جدیدترین</span>
<i>
<ArrowUpLeft />
</i>
</NextLink>
<div className="product-card__body">
<small>{product.category}</small>
<h3>
<NextLink href={product.href}>{product.title}</NextLink>
</h3>
<p dir="ltr">{product.en}</p>
<div>
<code>{product.code}</code>
<a href="tel:+982188530256">
استعلام قیمت
<Phone size={15} />
</a>
</div>
</div>
</article>
))}
</div>
</div>
</section>
<section className="about" id="about">
<div className="container about__grid">
<div className="about__visual">
{aboutMainImage?.linkUrl ? (
<a
className="about__main-image"
href={aboutMainImage.linkUrl}
style={{
aspectRatio: cssAspectRatio(aboutImages?.aspectRatio),
}}
>
<img
src={aboutMainImage.url}
alt={aboutMainImage.titleFa || "تجهیزات صنعتی سامان اندیش"}
/>
</a>
) : (
<img
src={aboutMainImage?.url || `${assetBase}/trEfzlNgmz.jpg`}
alt={aboutMainImage?.titleFa || "تجهیزات صنعتی سامان اندیش"}
style={
aboutMainImage
? {
aspectRatio: cssAspectRatio(aboutImages?.aspectRatio),
height: "auto",
}
: undefined
}
/>
)}
<div
className="about__small-image"
style={
aboutSecondaryImage
? {
aspectRatio: cssAspectRatio(aboutImages?.aspectRatio),
height: "auto",
}
: undefined
}
>
{aboutSecondaryImage?.linkUrl ? (
<a href={aboutSecondaryImage.linkUrl}>
<img
src={aboutSecondaryImage.url}
alt={aboutSecondaryImage.titleFa || "ماشین‌آلات صنعت کابل"}
/>
</a>
) : (
<img
src={
aboutSecondaryImage?.url || `${assetBase}/rhipZeVqzD.jpg`
}
alt={
aboutSecondaryImage?.titleFa || "ماشین‌آلات صنعت کابل"
}
/>
)}
</div>
<div className="experience-badge">
<strong>۲۵</strong>
<span>سال تجربه تخصصی</span>
</div>
</div>
<div className="about__content">
<SectionTitle eyebrow="ABOUT SAMAN ANDISH" title="فراتر از تأمین‌کننده ماشین‌آلات" />
<p className="about__lead">
سامان اندیش، همراه فنی و تجاری تولیدکنندگان سیم و کابل در انتخاب
هوشمندانه تجهیزات و توسعه خطوط تولید است.
</p>
<p>
با تکیه بر تجربه اجرایی، شبکه بین‌المللی و شناخت دقیق فرآیندهای
تولید، راهکاری متناسب با ظرفیت، بودجه و اهداف هر کارخانه ارائه
می‌کنیم.
</p>
<div className="about__features">
<div>
<ShieldCheck />
<span>
<strong>تضمین اصالت</strong>
ارزیابی فنی پیش از خرید
</span>
</div>
<div>
<Settings />
<span>
<strong>پشتیبانی اجرایی</strong>
نصب، راه‌اندازی و آموزش
</span>
</div>
</div>
<a className="button button--navy" href="/about-us">
بیشتر درباره ما
<ArrowLeft size={18} />
</a>
</div>
</div>
</section>
<section className="values">
<div className="container values__grid">
{[
[Globe2, "رقابت در بازار جهانی", "GLOBAL COMPETITION"],
[Gauge, "کیفیت استاندارد", "STANDARD QUALITY"],
[BriefcaseBusiness, "مهندسی نوآورانه", "SMART ENGINEERING"],
[FlaskConical, "تکنولوژی پیشرفته", "ADVANCED TECHNOLOGY"],
].map(([Icon, title, en], index) => {
const ValueIcon = Icon as typeof Globe2;
return (
<div className="value-card" key={title as string}>
<span>0{index + 1}</span>
<ValueIcon />
<h3>{title as string}</h3>
<p>{en as string}</p>
</div>
);
})}
</div>
</section>
<section className="news" id="news">
<div className="container">
<div className="section-heading-row">
<SectionTitle eyebrow="INSIGHTS & NEWS" title="دانش روز صنعت" />
<a className="text-link" href="#">
مشاهده همه اخبار
<ArrowLeft size={18} />
</a>
</div>
<div className="news-grid">
{news.map((item, index) => (
<article className={`news-card ${index === 0 ? "news-card--featured" : ""}`} key={item.title}>
<a className="news-card__image" href="#">
<img src={item.image} alt={item.title} />
</a>
<div className="news-card__body">
<div>
<span>{item.tag}</span>
<time>{item.date}</time>
</div>
<h3>{item.title}</h3>
<p>
تازه‌ترین تحولات، فناوری‌ها و رویدادهای تخصصی صنعت سیم و
کابل را با تحلیل کارشناسان سامان اندیش دنبال کنید.
</p>
<a href="#">
ادامه مطلب
<ArrowLeft size={17} />
</a>
</div>
</article>
))}
</div>
</div>
</section>
<section className="customers">
<div className="container">
<SectionTitle eyebrow="TRUSTED BY INDUSTRY" title="مشتریان ما" />
<div className="logo-marquee">
<div>
{[...customerLogos, ...customerLogos].map((logo, index) => (
<span key={`${logo}-${index}`}>
<img src={`${assetBase}/${logo}`} alt="نشان مشتری سامان اندیش" />
</span>
))}
</div>
</div>
</div>
</section>
<section className="cta-strip">
<div className="container cta-strip__inner">
<div>
<SlidersHorizontal />
<span>
<small>برای ارتقای خط تولید آماده‌اید؟</small>
<strong>ماشین مناسب را با کمک متخصصان ما پیدا کنید.</strong>
</span>
</div>
<a className="button button--white" href="/contact-us">
شروع گفتگو
<ArrowLeft size={18} />
</a>
</div>
</section>
<footer className="footer" id="contact">
<div className="footer__pattern" />
<div className="container footer__grid">
<div className="footer__brand">
<img src={`${assetBase}/4eEq8S7FxV.png`} alt="سامان اندیش" />
<p>
تأمین‌کننده تخصصی ماشین‌آلات و راهکارهای فنی صنعت سیم و کابل، با
تمرکز بر کیفیت، بهره‌وری و همکاری بلندمدت.
</p>
<div>
<a href="#" aria-label="اینستاگرام">
<Camera />
</a>
<a href="#" aria-label="لینکدین">
<LinkIcon />
</a>
<a href="#" aria-label="تلگرام">
<MessageCircle />
</a>
</div>
</div>
<div className="footer__links">
<h3>دسترسی سریع</h3>
<a href="#">صفحه اصلی</a>
<NextLink href="/products">ماشین‌آلات</NextLink>
<a href="#news">اخبار و مقالات</a>
<a href="/about-us">درباره ما</a>
<a href="/contact-us">تماس با ما</a>
</div>
<div className="footer__links">
<h3>خدمات ما</h3>
<a href="#">تأمین ماشین‌آلات</a>
<a href="#">خدمات فنی و مهندسی</a>
<a href="#">مواد اولیه</a>
<a href="/contact-us">مشاوره خرید</a>
</div>
<div className="footer__contact">
<h3>با ما در ارتباط باشید</h3>
<a href="tel:+982188530256">
<span>
<Phone />
</span>
<div>
<small>شماره تماس</small>
<strong dir="ltr">021 8853 0256</strong>
</div>
</a>
<a href="mailto:info@samanandish.com">
<span>
<Mail />
</span>
<div>
<small>ایمیل</small>
<strong>info@samanandish.com</strong>
</div>
</a>
<div className="address">
<span>
<MapPin />
</span>
<p>تهران، خیابان مطهری، خیابان ترکمنستان</p>
</div>
</div>
</div>
<div className="container footer__bottom">
<p>© ۱۴۰۵ سامان اندیش — تمامی حقوق محفوظ است.</p>
<span dir="ltr">ENGINEERED FOR INDUSTRY</span>
</div>
</footer>
</main>
);
}