feat: refine storefront product and sale sections
This commit is contained in:
@@ -2,8 +2,10 @@ import { Fragment } from "react";
|
||||
import { Carousel } from "@/components/ui/Carousel";
|
||||
import { ProductCard } from "@/components/ui/ProductCard";
|
||||
import { SectionTitle } from "@/components/ui/SectionTitle";
|
||||
import { ChevronLeftIcon } from "@/components/ui/icons";
|
||||
import { TwoBanners } from "@/components/home/TwoBanners";
|
||||
import { ThreeBanners } from "@/components/home/ThreeBanners";
|
||||
import { SpecialSaleCountdown } from "@/components/home/SpecialSaleCountdown";
|
||||
import { productHref, type ProductGroup, type StaticImageWithUrl } from "@/lib/meshkee";
|
||||
|
||||
// The API only gives one `title` string per group (sometimes already
|
||||
@@ -19,6 +21,67 @@ function englishLabelFromKey(key: string): string | undefined {
|
||||
return /[a-zA-Z]/.test(label) ? label.toUpperCase() : undefined;
|
||||
}
|
||||
|
||||
function isSpecialSaleGroup(group: ProductGroup): boolean {
|
||||
return /فروش\s*ویژه|special[\s_-]*sale/i.test(`${group.title} ${group.key}`);
|
||||
}
|
||||
|
||||
function SpecialSaleSection({ group }: { group: ProductGroup }) {
|
||||
return (
|
||||
<section className="bg-red py-6 md:py-8">
|
||||
<div className="mx-auto flex max-w-7xl items-stretch gap-3 px-4 md:gap-5">
|
||||
<aside className="my-2 flex w-[calc(50%-1rem)] shrink-0 flex-col items-center justify-center rounded-2xl bg-red-dark/35 p-3 text-center text-white md:w-[240px] md:p-6">
|
||||
<svg
|
||||
viewBox="0 0 32 32"
|
||||
className="mb-3 h-11 w-11 md:h-14 md:w-14"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<g transform="matrix(1.05 0 0 1.05 -.8 -.8006)">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m30.25 14.182-.876-.876c-.36-.36-.577-1.168-.445-1.661l.32-1.197a2.54 2.54 0 0 0-.259-1.949 2.55 2.55 0 0 0-1.559-1.198l-1.196-.321c-.492-.132-1.084-.724-1.217-1.215l-.32-1.197C24.34 3.232 22.899 2.39 21.55 2.75l-1.196.321c-.459.122-1.316-.101-1.66-.445l-.877-.876c-.969-.969-2.666-.969-3.635 0l-.877.877c-.344.344-1.201.567-1.661.445l-1.195-.321c-1.349-.361-2.79.481-3.148 1.818l-.32 1.195c-.132.494-.724 1.085-1.216 1.217l-1.197.321c-.662.177-1.216.603-1.56 1.199s-.435 1.288-.257 1.947l.319 1.198c.132.493-.084 1.3-.444 1.661l-.876.876C1.267 14.667 1 15.313 1 16s.267 1.333.751 1.817l.876.876c.36.36.577 1.168.445 1.661l-.32 1.197a2.54 2.54 0 0 0 .259 1.949 2.55 2.55 0 0 0 1.559 1.198l1.195.32c.493.132 1.085.724 1.218 1.215l.319 1.197c.358 1.336 1.804 2.175 3.147 1.817l1.197-.32c.46-.122 1.317.101 1.661.445l.876.875c.484.486 1.13.753 1.818.753s1.334-.267 1.817-.75l.877-.876c.345-.344 1.205-.565 1.661-.445l1.195.32q.326.087.662.087h.001a2.575 2.575 0 0 0 2.485-1.905l.319-1.194c.133-.494.725-1.085 1.217-1.218l1.196-.32c.662-.177 1.216-.604 1.56-1.2s.435-1.288.257-1.947l-.319-1.198c-.132-.493.084-1.3.444-1.661l.876-.876c.484-.484.751-1.13.751-1.817a2.55 2.55 0 0 0-.75-1.818M9 12c0-1.654 1.346-3 3-3s3 1.346 3 3-1.346 3-3 3-3-1.346-3-3m3.707 8.707a.997.997 0 0 1-1.414 0 1 1 0 0 1 0-1.414l8-8a.999.999 0 1 1 1.414 1.414zM20 23c-1.654 0-3-1.346-3-3s1.346-3 3-3 3 1.346 3 3-1.346 3-3 3"
|
||||
/>
|
||||
<circle cx="20" cy="20" r="1" fill="currentColor" />
|
||||
<circle cx="12" cy="12" r="1" fill="currentColor" />
|
||||
</g>
|
||||
</svg>
|
||||
<h2 className="text-lg font-bold md:text-2xl">{group.title}</h2>
|
||||
<p className="mt-1.5 mb-4 text-xs text-white/80 md:text-sm">تا پایان فروش ویژه</p>
|
||||
<SpecialSaleCountdown />
|
||||
<a
|
||||
href={`/products?specialId=${group.id}`}
|
||||
className="mt-4 flex shrink-0 items-center gap-1.5 rounded-lg bg-white px-4 py-2 text-sm font-medium text-black transition-colors hover:bg-white/90"
|
||||
>
|
||||
مشاهده همه
|
||||
<ChevronLeftIcon className="h-4 w-4" />
|
||||
</a>
|
||||
</aside>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
<Carousel
|
||||
ariaLabel={`کاروسل ${group.title}`}
|
||||
slideClassName="!w-full md:!w-[240px]"
|
||||
>
|
||||
{group.items.map((product) => (
|
||||
<ProductCard
|
||||
key={product.id}
|
||||
name={product.name}
|
||||
brand={product.brand ?? ""}
|
||||
price={product.price}
|
||||
oldPrice={product.oldPrice}
|
||||
discount={product.discount}
|
||||
image={product.image ?? "/images/placeholder.svg"}
|
||||
href={productHref(product)}
|
||||
variant="sale"
|
||||
fluid
|
||||
/>
|
||||
))}
|
||||
</Carousel>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// Renders every admin-managed carousel from GET /store-specials — whatever
|
||||
// groups exist there, in their configured order. Nothing here is keyed off
|
||||
// a specific group id/key/title, so a new carousel created in the Meshkee
|
||||
@@ -55,33 +118,37 @@ export function StoreSpecialCarousels({
|
||||
{visibleGroups.map((group, i) => (
|
||||
<Fragment key={group.id}>
|
||||
{i > 0 && banners[(i - 1) % banners.length]}
|
||||
<section className="py-10 md:py-14">
|
||||
<div className="mx-auto max-w-7xl px-4">
|
||||
<SectionTitle
|
||||
title={group.title}
|
||||
enTitle={englishLabelFromKey(group.key)}
|
||||
action="مشاهده همه"
|
||||
actionHref={`/products?specialId=${group.id}`}
|
||||
/>
|
||||
<Carousel ariaLabel={`کاروسل ${group.title}`}>
|
||||
{group.items.map((product) => (
|
||||
<ProductCard
|
||||
key={product.id}
|
||||
name={product.name}
|
||||
brand={product.brand ?? ""}
|
||||
price={product.price}
|
||||
oldPrice={product.oldPrice}
|
||||
discount={product.discount}
|
||||
image={product.image ?? "/images/placeholder.svg"}
|
||||
href={productHref(product)}
|
||||
contactPhone={contactPhone}
|
||||
variantId={product.variantId}
|
||||
slug={product.slug}
|
||||
/>
|
||||
))}
|
||||
</Carousel>
|
||||
</div>
|
||||
</section>
|
||||
{isSpecialSaleGroup(group) ? (
|
||||
<SpecialSaleSection group={group} />
|
||||
) : (
|
||||
<section className="py-10 md:py-14">
|
||||
<div className="mx-auto max-w-7xl px-4">
|
||||
<SectionTitle
|
||||
title={group.title}
|
||||
enTitle={englishLabelFromKey(group.key)}
|
||||
action="مشاهده همه"
|
||||
actionHref={`/products?specialId=${group.id}`}
|
||||
/>
|
||||
<Carousel ariaLabel={`کاروسل ${group.title}`}>
|
||||
{group.items.map((product) => (
|
||||
<ProductCard
|
||||
key={product.id}
|
||||
name={product.name}
|
||||
brand={product.brand ?? ""}
|
||||
price={product.price}
|
||||
oldPrice={product.oldPrice}
|
||||
discount={product.discount}
|
||||
image={product.image ?? "/images/placeholder.svg"}
|
||||
href={productHref(product)}
|
||||
contactPhone={contactPhone}
|
||||
variantId={product.variantId}
|
||||
slug={product.slug}
|
||||
/>
|
||||
))}
|
||||
</Carousel>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user