729 lines
20 KiB
TypeScript
729 lines
20 KiB
TypeScript
"use client";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
import { useCallback, useEffect, useState } from "react";
|
|
import type { ControllerRenderProps, UseFormReturn } from "react-hook-form";
|
|
import { Controller, useForm } from "react-hook-form";
|
|
import z from "zod";
|
|
|
|
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardFooter,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import type { CarouselApi } from "@/components/ui/carousel";
|
|
import {
|
|
Carousel,
|
|
CarouselContent,
|
|
CarouselItem,
|
|
} from "@/components/ui/carousel";
|
|
import { Field, FieldLabel } from "@/components/ui/field";
|
|
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const STOCK_STATUS = {
|
|
IN_STOCK: "IN_STOCK",
|
|
OUT_OF_STOCK: "OUT_OF_STOCK",
|
|
} as const;
|
|
|
|
type StockStatusCode = keyof typeof STOCK_STATUS;
|
|
|
|
interface ListCarouselProps {
|
|
productsList: Array<ProductCard>;
|
|
}
|
|
|
|
type Image = {
|
|
src: string;
|
|
alt: string;
|
|
};
|
|
|
|
type Option = {
|
|
id: string;
|
|
label: string;
|
|
color: string;
|
|
value: string;
|
|
image: Image;
|
|
};
|
|
|
|
interface Product {
|
|
name: string;
|
|
collection: {
|
|
label: string;
|
|
link: string;
|
|
};
|
|
variantId: string;
|
|
image: {
|
|
src: string;
|
|
alt: string;
|
|
};
|
|
link: string;
|
|
price: ProductPrice;
|
|
quickAdd?: boolean;
|
|
variants?: {
|
|
name: FieldName;
|
|
options: Array<Option>;
|
|
};
|
|
stockStatusCode: StockStatusCode;
|
|
}
|
|
|
|
type ProductCard = Product;
|
|
|
|
type ProductCardProps = ProductCard;
|
|
|
|
interface ProductPrice {
|
|
regular: number;
|
|
sale?: number;
|
|
currency: string;
|
|
}
|
|
|
|
type FieldName = keyof FormType;
|
|
|
|
type FormType = z.infer<typeof formSchema>;
|
|
|
|
interface ProductFormProps {
|
|
colorHinges?: {
|
|
name: FieldName;
|
|
options: Omit<Option, "image">[];
|
|
};
|
|
form: UseFormReturn<FormType>;
|
|
}
|
|
|
|
interface RadioGroupProps {
|
|
options?: Omit<Option, "image">[];
|
|
field: ControllerRenderProps<FormType>;
|
|
}
|
|
|
|
type ColorOptionProps = Omit<Option, "image">;
|
|
|
|
type ProductList = Array<Product>;
|
|
|
|
const PRODUCTS_LIST: ProductList = [
|
|
{
|
|
collection: {
|
|
label: "رنگهای بهاری",
|
|
link: "#",
|
|
},
|
|
name: "کاور مینت گارد",
|
|
variantId: "mint_guard",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung--uF6u5Cmnsw-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح سبز نعنایی",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "OUT_OF_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "رنگهای انتزاعی",
|
|
link: "#",
|
|
},
|
|
name: "کاور خط نیمهشب",
|
|
variantId: "midnight_black-1",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-CubSQS4iYEE-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح خطی تیره",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
sale: 490000.0,
|
|
currency: "ریال",
|
|
},
|
|
variants: {
|
|
name: "color",
|
|
options: [
|
|
{
|
|
id: "midnight_black-1",
|
|
label: "مشکی نیمهشب",
|
|
color: "#0B0B0B",
|
|
value: "midnight_black",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-CubSQS4iYEE-unsplash-3.jpg",
|
|
alt: "کاور گوشی مشکی",
|
|
},
|
|
},
|
|
{
|
|
id: "royal_indigo-1",
|
|
label: "نیلی سلطنتی",
|
|
color: "#3B0A45",
|
|
value: "royal_indigo",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-wyEh0ZnwUFM-unsplash-3.jpg",
|
|
alt: "کاور گوشی نیلی",
|
|
},
|
|
},
|
|
{
|
|
id: "vibrant_tangerine-1",
|
|
label: "نارنجی درخشان",
|
|
color: "#FF6400",
|
|
value: "vibrant_tangerine",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-kfv-hU9ZVkk-unsplash-3.jpg",
|
|
alt: "کاور گوشی نارنجی",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "سایههای آرامش",
|
|
link: "#",
|
|
},
|
|
name: "کاور درخشش آمتیست",
|
|
variantId: "pastel_lavender",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-EVL1nwmdg_g-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح بنفش ملایم",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
variants: {
|
|
name: "color",
|
|
options: [
|
|
{
|
|
id: "pastel_charcoal-1",
|
|
label: "خاکستری زغالی",
|
|
color: "#4A4A4A",
|
|
value: "pastel_charcoal",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-h_t-VwFFLAY-unsplash-3.jpg",
|
|
alt: "کاور گوشی خاکستری",
|
|
},
|
|
},
|
|
{
|
|
id: "pastel_lavender-1",
|
|
label: "اسطوخودوس",
|
|
color: "#CBA4DB",
|
|
value: "pastel_lavender",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-EVL1nwmdg_g-unsplash-3.jpg",
|
|
alt: "کاور گوشی یاسی",
|
|
},
|
|
},
|
|
{
|
|
id: "pastel_peach-1",
|
|
label: "هلویی",
|
|
color: "#FFDAB9",
|
|
value: "pastel_peach",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-f-scc2htKNQ-unsplash-3.jpg",
|
|
alt: "کاور گوشی هلویی",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "هنر در حرکت",
|
|
link: "#",
|
|
},
|
|
name: "کاور هجوم رنگ",
|
|
variantId: "color_rush",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-f-scc2htKNQ-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح رنگی پویا",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "افقهای مرتفع",
|
|
link: "#",
|
|
},
|
|
name: "کاور قلههای تکرنگ",
|
|
variantId: "monochrome_peaks",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-fCO3tBcnkhg-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح کوهستان تکرنگ",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "ریمیکس رترو",
|
|
link: "#",
|
|
},
|
|
name: "کاور پیچ و تورفتگی",
|
|
variantId: "twist_&_grid",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-h_t-VwFFLAY-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح شبکهای رترو",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "بیانهای جسورانه",
|
|
link: "#",
|
|
},
|
|
name: "کاور کلمات مرکباتی",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-kfv-hU9ZVkk-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح تایپوگرافی",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
variants: {
|
|
name: "color",
|
|
options: [
|
|
{
|
|
id: "midnight_black-2",
|
|
label: "مشکی نیمهشب",
|
|
color: "#0B0B0B",
|
|
value: "midnight_black",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-OXW7AuNsKVI-unsplash-3.jpg",
|
|
alt: "کاور گوشی مشکی",
|
|
},
|
|
},
|
|
{
|
|
id: "royal_indigo-2",
|
|
label: "نیلی سلطنتی",
|
|
color: "#3B0A45",
|
|
value: "royal_indigo",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-wyEh0ZnwUFM-unsplash-3.jpg",
|
|
alt: "کاور گوشی نیلی",
|
|
},
|
|
},
|
|
{
|
|
id: "vibrant_tangerine-2",
|
|
label: "نارنجی درخشان",
|
|
color: "#FF6400",
|
|
value: "vibrant_tangerine",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-kfv-hU9ZVkk-unsplash-3.jpg",
|
|
alt: "کاور گوشی نارنجی",
|
|
},
|
|
},
|
|
],
|
|
},
|
|
variantId: "vibrant_tangerine-2",
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "فراتر از افق",
|
|
link: "#",
|
|
},
|
|
name: "کاور سوار بر آسمان",
|
|
variantId: "sky_drift",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-mAUQI0qnKGw-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح آسمان",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "سری آسمانی",
|
|
link: "#",
|
|
},
|
|
name: "کاور پرده نیمهشب",
|
|
variantId: "midnight_veil",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-OXW7AuNsKVI-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح شبانه",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
{
|
|
collection: {
|
|
label: "رویاهای ساحلی",
|
|
link: "#",
|
|
},
|
|
name: "کاور زمزمه جزر و مد",
|
|
variantId: "tide_whisper",
|
|
link: "#",
|
|
image: {
|
|
src: "/block-media/shadcnblocks/block/ecommerce/other/jascent-leung-wyEh0ZnwUFM-unsplash-3.jpg",
|
|
alt: "کاور گوشی با طرح ساحلی",
|
|
},
|
|
price: {
|
|
regular: 590000.0,
|
|
currency: "ریال",
|
|
},
|
|
|
|
quickAdd: true,
|
|
stockStatusCode: "IN_STOCK",
|
|
},
|
|
];
|
|
|
|
interface ProductList6Props {
|
|
className?: string;
|
|
}
|
|
|
|
const ProductList6 = ({ className }: ProductList6Props) => {
|
|
return (
|
|
<section className={cn("py-32", className)}>
|
|
<div className="container">
|
|
<ListCarousel productsList={PRODUCTS_LIST} />
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
const ListCarousel = ({ productsList }: ListCarouselProps) => {
|
|
const [api, setApi] = useState<CarouselApi>();
|
|
const [current, setCurrent] = useState(0);
|
|
const [canScrollPrev, setCanScrollPrev] = useState(false);
|
|
const [canScrollNext, setCanScrollNext] = useState(false);
|
|
|
|
const onSelect = useCallback((carouselApi: CarouselApi) => {
|
|
if (!carouselApi) return;
|
|
setCanScrollPrev(carouselApi.canScrollPrev());
|
|
setCanScrollNext(carouselApi.canScrollNext());
|
|
setCurrent(carouselApi.selectedScrollSnap() + 1);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!api) return;
|
|
|
|
onSelect(api);
|
|
api.on("reInit", onSelect);
|
|
api.on("select", onSelect);
|
|
|
|
return () => {
|
|
api.off("select", onSelect);
|
|
};
|
|
}, [api, onSelect]);
|
|
|
|
if (!productsList) return;
|
|
|
|
return (
|
|
<Carousel
|
|
dir="rtl"
|
|
opts={{
|
|
align: "start",
|
|
direction: "rtl",
|
|
}}
|
|
setApi={setApi}
|
|
>
|
|
<div className="flex items-center justify-between pb-6">
|
|
<div className="flex flex-wrap items-end gap-2">
|
|
<h2 className="text-lg leading-tight font-black uppercase xl:text-xl">
|
|
محبوبترین
|
|
</h2>
|
|
<p className="text-sm font-medium text-muted-foreground xl:text-base">
|
|
تا ۳۰٪ تخفیف
|
|
</p>
|
|
</div>
|
|
<div className="hidden items-center gap-2 xl:flex">
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon-sm"
|
|
className="static size-8 shrink-0 translate-0 touch-manipulation"
|
|
disabled={!canScrollPrev}
|
|
onClick={() => api?.scrollPrev()}
|
|
aria-label="اسلاید قبلی"
|
|
>
|
|
<ChevronRight className="size-4" />
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="icon-sm"
|
|
className="static size-8 shrink-0 translate-0 touch-manipulation"
|
|
disabled={!canScrollNext}
|
|
onClick={() => api?.scrollNext()}
|
|
aria-label="اسلاید بعدی"
|
|
>
|
|
<ChevronLeft className="size-4" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
<CarouselContent className="ms-0 -me-4">
|
|
{productsList.map((product, index) => (
|
|
<CarouselItem
|
|
key={`product-list-6-item-${index}`}
|
|
className="ps-0 pe-4 sm:basis-1/2 xl:basis-1/4"
|
|
>
|
|
<ProductCard {...product} />
|
|
</CarouselItem>
|
|
))}
|
|
</CarouselContent>
|
|
<div className="flex items-center justify-center pt-5 xl:hidden">
|
|
{productsList.map((_, index) => (
|
|
<button
|
|
onClick={() => api?.scrollTo(index)}
|
|
data-active={current === index + 1}
|
|
className="flex size-6 after:m-auto after:h-1 after:w-5 after:bg-muted-foreground/50 after:transition-colors data-[active=true]:after:bg-primary sm:last:hidden"
|
|
key={`product-list-6-pagination-bullets-${index}`}
|
|
></button>
|
|
))}
|
|
</div>
|
|
</Carousel>
|
|
);
|
|
};
|
|
|
|
const formSchema = z.object({
|
|
color: z.string(),
|
|
});
|
|
|
|
const ProductCard = ({
|
|
name,
|
|
image,
|
|
stockStatusCode,
|
|
price,
|
|
link,
|
|
collection,
|
|
quickAdd,
|
|
variants,
|
|
variantId,
|
|
}: ProductCardProps) => {
|
|
const form = useForm<FormType>({
|
|
resolver: zodResolver(formSchema),
|
|
defaultValues: {
|
|
color: variantId,
|
|
},
|
|
});
|
|
const selectedVariant = form.watch("color");
|
|
const currentVariantImage =
|
|
variants?.options.find((item) => item.value === selectedVariant)?.image ||
|
|
image;
|
|
|
|
const { regular, sale, currency } = price;
|
|
|
|
function onSubmit(values: FormType) {
|
|
console.log(values);
|
|
}
|
|
|
|
return (
|
|
<Card className="group gap-2 rounded-none border-none bg-background p-0 shadow-none">
|
|
<CardHeader className="gap-0 p-0">
|
|
<AspectRatio ratio={0.76984127} className="overflow-hidden">
|
|
<a href={link} className="block size-full">
|
|
{currentVariantImage && (
|
|
<img
|
|
src={currentVariantImage?.src}
|
|
alt={currentVariantImage?.alt}
|
|
className="block size-full object-cover object-center transition-transform duration-500 group-hover:scale-110"
|
|
/>
|
|
)}
|
|
</a>
|
|
{quickAdd && stockStatusCode === STOCK_STATUS.IN_STOCK && (
|
|
<div className="absolute inset-x-5 bottom-5 transition-opacity duration-400 group-hover:opacity-100 xl:opacity-0">
|
|
<Button
|
|
onClick={() => {
|
|
form.trigger().then((valid) => {
|
|
if (valid) {
|
|
form.handleSubmit(onSubmit)();
|
|
}
|
|
});
|
|
}}
|
|
className="w-full"
|
|
>
|
|
افزودن سریع
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</AspectRatio>
|
|
</CardHeader>
|
|
<CardContent className="p-0">
|
|
{(stockStatusCode || price?.sale) && (
|
|
<div className="text-sm">
|
|
{stockStatusCode === STOCK_STATUS.OUT_OF_STOCK ? (
|
|
<span className="text-muted-foreground">فروخته شد</span>
|
|
) : (
|
|
price.sale && <span className="text-destructive">حراج</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
<div
|
|
className={`pt-0.5 ${stockStatusCode === STOCK_STATUS.IN_STOCK && !price?.sale && "mt-5"}`}
|
|
>
|
|
<a className="text-sm font-bold" href={collection.link}>
|
|
{collection.label}
|
|
</a>
|
|
<div className="flex flex-wrap items-center gap-2 xl:flex-nowrap">
|
|
<CardTitle className="text-sm font-normal">{name}</CardTitle>
|
|
<div className="transition-opacity duration-400 group-hover:opacity-100 xl:opacity-0">
|
|
<ProductForm colorHinges={variants} form={form} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter className="relative mt-1 flex-col items-start gap-3 p-0 xl:flex-row">
|
|
<div className="transition-opacity duration-400 xl:group-hover:opacity-0">
|
|
<ProductCardPrice
|
|
regular={regular}
|
|
sale={sale}
|
|
currency={currency}
|
|
/>
|
|
</div>
|
|
<div className="absolute flex items-center gap-3 bg-background opacity-0 transition-opacity duration-400 group-hover:opacity-100">
|
|
<Button variant="link" className="h-fit p-0 underline">
|
|
همین حالا بخرید
|
|
</Button>
|
|
<Button
|
|
variant="link"
|
|
className="h-fit p-0 text-muted-foreground underline"
|
|
>
|
|
مشاهده سریع
|
|
</Button>
|
|
</div>
|
|
</CardFooter>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
const ProductForm = ({ colorHinges, form }: ProductFormProps) => {
|
|
if (!colorHinges) return;
|
|
|
|
return (
|
|
<form>
|
|
<Controller
|
|
control={form.control}
|
|
name={colorHinges.name}
|
|
render={({ field }) => (
|
|
<Field>
|
|
<ColorRadioGroup field={field} options={colorHinges.options} />
|
|
</Field>
|
|
)}
|
|
/>
|
|
</form>
|
|
);
|
|
};
|
|
|
|
const ColorRadioGroup = ({ options, field }: RadioGroupProps) => {
|
|
if (!options) return;
|
|
|
|
return (
|
|
<RadioGroup
|
|
{...field}
|
|
value={`${field.value}`}
|
|
onValueChange={(value) => {
|
|
if (value != field.value && value) {
|
|
field.onChange(value);
|
|
}
|
|
}}
|
|
className="flex gap-0"
|
|
>
|
|
{options &&
|
|
options.map((item, index) => (
|
|
<Field key={`product-list-6-color-input-${index}`}>
|
|
<ColorOption
|
|
label={item.label}
|
|
id={item.id}
|
|
color={item.color}
|
|
value={item.value}
|
|
/>
|
|
</Field>
|
|
))}
|
|
</RadioGroup>
|
|
);
|
|
};
|
|
|
|
const ColorOption = ({ id, value, color, label }: ColorOptionProps) => {
|
|
return (
|
|
<FieldLabel
|
|
htmlFor={id}
|
|
aria-label={label}
|
|
className="relative flex size-5 cursor-pointer after:pointer-events-none after:absolute after:inset-1/2 after:size-1 after:-translate-1/2 after:rounded-full after:bg-white after:opacity-0 after:transition-opacity has-checked:after:opacity-100"
|
|
>
|
|
<RadioGroupItem id={id} className="sr-only" value={value} />
|
|
<div
|
|
style={{
|
|
backgroundColor: color,
|
|
}}
|
|
className="m-auto size-2.5 rounded-full"
|
|
></div>
|
|
</FieldLabel>
|
|
);
|
|
};
|
|
|
|
const formatCurrency = (
|
|
value: number,
|
|
currency: string = "ریال",
|
|
locale: string = "fa-IR",
|
|
) => {
|
|
return `${value.toLocaleString(locale)} ${currency}`;
|
|
};
|
|
|
|
const ProductCardPrice = ({
|
|
regular,
|
|
sale,
|
|
currency,
|
|
}: {
|
|
regular: number;
|
|
sale?: number;
|
|
currency: string;
|
|
}) => {
|
|
const onSale = sale != null;
|
|
|
|
return (
|
|
<div className="flex flex-wrap items-center gap-x-2 text-sm font-medium">
|
|
{onSale ? (
|
|
<span className="leading-tight text-foreground">
|
|
{formatCurrency(sale, currency)}
|
|
</span>
|
|
) : null}
|
|
<span
|
|
className={cn(
|
|
"leading-tight",
|
|
onSale
|
|
? "text-muted-foreground line-through"
|
|
: "text-foreground",
|
|
)}
|
|
>
|
|
{formatCurrency(regular, currency)}
|
|
</span>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export { ProductList6 };
|