130 lines
3.2 KiB
TypeScript
130 lines
3.2 KiB
TypeScript
"use client";
|
||
|
||
import Image from "next/image";
|
||
import Link from "next/link";
|
||
import { useRef } from "react";
|
||
|
||
type Product = {
|
||
title: string;
|
||
enName: string;
|
||
price: string;
|
||
image: string;
|
||
href?: string;
|
||
};
|
||
|
||
type SaleBanner = {
|
||
image?: string | null;
|
||
linkUrl?: string | null;
|
||
ariaLabel?: string;
|
||
};
|
||
|
||
function ProductPrice({ price }: { price: string }) {
|
||
const trimmed = price.trim();
|
||
const match = trimmed.match(/^([\d٬,.\s]+)\s*(تومان)$/);
|
||
|
||
if (match) {
|
||
return (
|
||
<p className="price price-with-amount" dir="ltr">
|
||
<span className="price-amount ltr-number" lang="en">
|
||
{match[1].trim()}
|
||
</span>
|
||
<span className="price-unit">تومان</span>
|
||
</p>
|
||
);
|
||
}
|
||
|
||
return <p className="price price-note">{trimmed}</p>;
|
||
}
|
||
|
||
function ProductCard({ product }: { product: Product }) {
|
||
return (
|
||
<Link
|
||
href={product.href || "/store"}
|
||
className="product-card"
|
||
aria-label={product.title}
|
||
>
|
||
<Image src={product.image} alt={product.title} width={560} height={560} />
|
||
<div className="product-body">
|
||
<p className="product-en-name">{product.enName}</p>
|
||
<h3>{product.title}</h3>
|
||
<ProductPrice price={product.price} />
|
||
</div>
|
||
</Link>
|
||
);
|
||
}
|
||
|
||
function SaleBannerCard({ banner }: { banner?: SaleBanner | null }) {
|
||
const style = banner?.image
|
||
? {
|
||
backgroundImage: `linear-gradient(90deg, rgba(136, 63, 63, 0.55), rgba(0, 35, 75, 0.5)), url(${banner.image})`,
|
||
backgroundSize: "cover",
|
||
backgroundPosition: "center",
|
||
}
|
||
: undefined;
|
||
const className = "sale sale-promo-card";
|
||
const label = banner?.ariaLabel || "فروش ویژه";
|
||
|
||
if (banner?.linkUrl) {
|
||
return (
|
||
<a href={banner.linkUrl} className={className} style={style} aria-label={label} />
|
||
);
|
||
}
|
||
|
||
return (
|
||
<Link href="/store" className={className} style={style} aria-label={label} />
|
||
);
|
||
}
|
||
|
||
export function ProductCarousel({
|
||
products,
|
||
ariaLabel,
|
||
withSaleBanner = false,
|
||
saleBanner,
|
||
}: {
|
||
products: Product[];
|
||
ariaLabel: string;
|
||
withSaleBanner?: boolean;
|
||
saleBanner?: SaleBanner | null;
|
||
}) {
|
||
const carouselRef = useRef<HTMLDivElement>(null);
|
||
|
||
const scrollCarousel = (direction: "next" | "previous") => {
|
||
const amount = 300;
|
||
carouselRef.current?.scrollBy({
|
||
left: direction === "next" ? -amount : amount,
|
||
behavior: "smooth",
|
||
});
|
||
};
|
||
|
||
return (
|
||
<div className="product-carousel-shell">
|
||
<button
|
||
className="carousel-chevron carousel-chevron-left"
|
||
type="button"
|
||
aria-label="محصولات بعدی"
|
||
onClick={() => scrollCarousel("next")}
|
||
>
|
||
‹
|
||
</button>
|
||
<div
|
||
ref={carouselRef}
|
||
className={`carousel ${withSaleBanner ? "sale-carousel" : ""}`}
|
||
aria-label={ariaLabel}
|
||
>
|
||
{withSaleBanner ? <SaleBannerCard banner={saleBanner} /> : null}
|
||
{products.map((product) => (
|
||
<ProductCard key={`${ariaLabel}-${product.title}`} product={product} />
|
||
))}
|
||
</div>
|
||
<button
|
||
className="carousel-chevron carousel-chevron-right"
|
||
type="button"
|
||
aria-label="محصولات قبلی"
|
||
onClick={() => scrollCarousel("previous")}
|
||
>
|
||
›
|
||
</button>
|
||
</div>
|
||
);
|
||
}
|