Products, blog, search, mega menu, and home carousels now read live tenant data, and login goes to the customer portal. Co-authored-by: Cursor <cursoragent@cursor.com>
129 lines
3.5 KiB
TypeScript
129 lines
3.5 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="flex w-[280px] min-h-[320px] shrink-0 snap-start self-stretch overflow-hidden rounded-3xl sm:w-[300px]"
|
|
aria-label="بنر فروش ویژه"
|
|
>
|
|
<SlotImage
|
|
image={saleBanner}
|
|
fallbackSrc="/images/sale-banner.png"
|
|
fallbackAlt="فروش ویژه"
|
|
aspectRatio={bannerSlot?.aspectRatio}
|
|
width={240}
|
|
height={400}
|
|
rounded="rounded-3xl"
|
|
fill
|
|
className="h-full w-full"
|
|
fallbackHref="/special-sale"
|
|
/>
|
|
</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>
|
|
);
|
|
}
|