Files
nini-naderi/src/components/ProductCarousel.tsx
T
Alireza HassaniandCursor 051939c6e2 Tighten storefront cards and rebuild the product page.
Match carousel cards on XL, add gallery zoom/lightbox, quantity and comments, and update the sale banner slot to 2:3.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-18 16:19:29 +03:30

131 lines
3.6 KiB
TypeScript

"use client";
import Link from "next/link";
import { useEffect, useState } from "react";
import type { Product } from "@/lib/catalog";
import { slotImages, type StaticImageSlot } from "@/lib/static-images";
import { Carousel } from "./Carousel";
import { ProductCard } from "./ProductCard";
import { SectionHeading } from "./SectionHeading";
import { SlotImage } from "./SlotImage";
type Props = {
id?: string;
english: string;
title: string;
products: Product[];
countdown?: boolean;
leadBanner?: boolean;
bannerSlot?: StaticImageSlot;
href?: string;
};
export function ProductCarousel({
id,
english,
title,
products,
countdown,
leadBanner,
bannerSlot,
href,
}: Props) {
const saleBanner = slotImages(bannerSlot)[0];
if (!products.length && !leadBanner) return null;
return (
<section id={id} className="container-nn py-8">
<SectionHeading english={english} title={title}>
{countdown ? <SaleTimer /> : null}
{href ? (
<Link href={href} className="text-sm font-bold text-purple-700 hover:text-purple-800">
مشاهده همه
</Link>
) : null}
</SectionHeading>
<Carousel>
{leadBanner ? (
<div
data-card
className="carousel-card relative min-h-0 self-stretch overflow-hidden rounded-3xl"
aria-label="بنر فروش ویژه"
>
<div className="absolute inset-0">
<SlotImage
image={saleBanner}
fallbackSrc="/images/sale-banner.png"
fallbackAlt="فروش ویژه"
aspectRatio={bannerSlot?.aspectRatio ?? "2:3"}
width={260}
height={390}
rounded="rounded-3xl"
fill
className="h-full w-full object-cover"
fallbackHref="/special-sale"
/>
</div>
</div>
) : null}
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</Carousel>
</section>
);
}
function SaleTimer() {
const [left, setLeft] = useState({ h: 11, m: 42, s: 18 });
useEffect(() => {
const timer = window.setInterval(() => {
setLeft((current) => {
let { h, m, s } = current;
if (s > 0) s -= 1;
else if (m > 0) {
s = 59;
m -= 1;
} else if (h > 0) {
s = 59;
m = 59;
h -= 1;
} else {
h = 11;
m = 59;
s = 59;
}
return { h, m, s };
});
}, 1000);
return () => window.clearInterval(timer);
}, []);
const cells = [
{ label: "ساعت", value: left.h },
{ label: "دقیقه", value: left.m },
{ label: "ثانیه", value: left.s },
];
return (
<div className="flex items-end gap-1.5">
{cells.map((cell, index) => (
<div key={cell.label} className="flex items-end gap-1.5">
{index > 0 ? (
<span className="font-display mb-5 text-xl leading-none text-rose-400">:</span>
) : null}
<div className="min-w-[3.25rem] rounded-2xl bg-linear-to-b from-rose-100 to-rose-50 px-2.5 py-2 text-center shadow-[inset_0_1px_0_rgb(255_255_255_/_0.8)]">
<p
dir="ltr"
lang="en"
className="font-display text-[1.65rem] font-semibold leading-none tracking-tight text-rose-600"
>
{String(cell.value).padStart(2, "0")}
</p>
<p className="mt-1 text-[10px] font-medium text-rose-400">{cell.label}</p>
</div>
</div>
))}
</div>
);
}