Create Glassmond storefront with Meshkee integration
Build the RTL Next.js experience with live catalog, journal, customer interactions, and SEO-ready canonical routes. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { Phone } from "lucide-react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
productImage,
|
||||
productUrl,
|
||||
type Product,
|
||||
} from "@/lib/meshkee";
|
||||
|
||||
function getPageSize() {
|
||||
if (typeof window === "undefined") return 5;
|
||||
if (window.innerWidth < 620) return 1;
|
||||
if (window.innerWidth < 900) return 2;
|
||||
return 5;
|
||||
}
|
||||
|
||||
export function ProductCarousel({ products }: { products: Product[] }) {
|
||||
const [pageSize, setPageSize] = useState(5);
|
||||
const [page, setPage] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const update = () => {
|
||||
setPageSize(getPageSize());
|
||||
setPage(0);
|
||||
};
|
||||
update();
|
||||
window.addEventListener("resize", update);
|
||||
return () => window.removeEventListener("resize", update);
|
||||
}, []);
|
||||
|
||||
const pages = Math.max(1, products.length - pageSize + 1);
|
||||
const visibleProducts = useMemo(() => {
|
||||
const start = page;
|
||||
return products.slice(start, start + pageSize);
|
||||
}, [page, pageSize]);
|
||||
|
||||
return (
|
||||
<div className="product-carousel">
|
||||
<div
|
||||
key={`${page}-${pageSize}`}
|
||||
className="source-products-grid"
|
||||
style={{ "--visible-products": pageSize } as React.CSSProperties}
|
||||
aria-live="polite"
|
||||
>
|
||||
{visibleProducts.map((product) => (
|
||||
<article className="source-product-card" key={product.id}>
|
||||
<div className="source-product-image">
|
||||
<Link href={productUrl(product)} aria-label={product.nameFa || product.title}>
|
||||
<Image
|
||||
src={productImage(product) || "/images/hero.jpg"}
|
||||
alt={product.nameFa || product.title}
|
||||
fill
|
||||
sizes="(max-width: 620px) 100vw, (max-width: 900px) 50vw, (max-width: 1200px) 33vw, 20vw"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="source-product-copy">
|
||||
<div>
|
||||
<h3>
|
||||
<Link href={productUrl(product)}>{product.nameFa || product.title}</Link>
|
||||
</h3>
|
||||
<a href="tel:+989301293902">تماس بگیرید</a>
|
||||
</div>
|
||||
<a
|
||||
className="product-phone"
|
||||
href="tel:+989301293902"
|
||||
aria-label={`تماس برای ${product.title}`}
|
||||
>
|
||||
<Phone />
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{pages > 1 && (
|
||||
<div className="carousel-dots" aria-label="صفحات محصولات">
|
||||
{Array.from({ length: pages }, (_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
className={index === page ? "active" : ""}
|
||||
onClick={() => setPage(index)}
|
||||
aria-label={`صفحه ${index + 1}`}
|
||||
aria-current={index === page ? "true" : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user