179 lines
5.4 KiB
TypeScript
179 lines
5.4 KiB
TypeScript
import { Check, Trash } from "lucide-react";
|
||
import { memo, useMemo } from "react";
|
||
import { QuantityControl } from "./QuantityControl";
|
||
import { formatPrice } from "../../utils/helpers";
|
||
import { CartItem as CartItemType } from "src/api/types";
|
||
import { isVideo } from "../MondrianProductList";
|
||
import { Button } from "../ui/button";
|
||
|
||
interface CartItemProps {
|
||
item: CartItemType;
|
||
onQuantityChange: (itemId: string, quantity: number) => void;
|
||
onRemoveItem: (itemId: string) => void;
|
||
isLoading: boolean;
|
||
hasBorder: boolean;
|
||
}
|
||
|
||
export const CartItem = memo(function CartItem({
|
||
item,
|
||
onQuantityChange,
|
||
onRemoveItem,
|
||
isLoading,
|
||
hasBorder,
|
||
}: CartItemProps) {
|
||
// Memoize discount calculation
|
||
const discountPercent = useMemo(() => {
|
||
if (!item.productVariant?.discountPrice || !item.productVariant?.price) {
|
||
return null;
|
||
}
|
||
return Math.round(
|
||
((Number(item.productVariant.price) -
|
||
Number(item.productVariant.discountPrice)) /
|
||
Number(item.productVariant.price)) *
|
||
100
|
||
);
|
||
}, [item.productVariant?.discountPrice, item.productVariant?.price]);
|
||
|
||
// Memoize video check
|
||
const isVideoMedia = useMemo(
|
||
() => isVideo(item.productVariant?.productImage || ""),
|
||
[item.productVariant?.productImage]
|
||
);
|
||
return (
|
||
<div
|
||
className={`flex p-4 gap-4 w-full flex-col ${hasBorder && "border-b"}`}
|
||
>
|
||
<div className="flex gap-2 w-full items-center">
|
||
{isVideoMedia ? (
|
||
<video
|
||
src={item.productVariant?.productImage}
|
||
className="rounded-[5px] w-[116px] h-[116px] object-cover"
|
||
muted
|
||
loop
|
||
autoPlay
|
||
playsInline
|
||
/>
|
||
) : (
|
||
<img
|
||
src={item.productVariant?.productImage}
|
||
alt={item.productVariant?.productTitle || ""}
|
||
className="rounded-[5px] w-[116px] h-[116px] object-cover"
|
||
loading="lazy"
|
||
/>
|
||
)}
|
||
<div className="flex flex-col gap-2">
|
||
<p className="text-B14 font-bold">
|
||
{item.productVariant?.productTitle || ""}
|
||
</p>
|
||
<p className="text-R12 text-GRAY line-clamp-1">
|
||
کد کالا: {item.productVariant?.id || ""}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<ColorSelector
|
||
color={
|
||
item.productVariant?.color?.value !== "UNSELECTED"
|
||
? item.productVariant?.color?.info?.detail || ""
|
||
: ""
|
||
}
|
||
/>
|
||
<SizeSelector
|
||
size={
|
||
item.productVariant?.size?.value !== "UNSELECTED"
|
||
? item.productVariant?.size?.info?.detail || ""
|
||
: ""
|
||
}
|
||
/>
|
||
|
||
<div className="flex w-full items-center justify-between">
|
||
<p className="text-B14 font-bold">قیمت واحد :</p>
|
||
<p className="text-B14 font-bold">
|
||
{formatPrice(item.productVariant?.price || "")} تومان
|
||
</p>
|
||
</div>
|
||
|
||
{discountPercent ? (
|
||
<div className="flex w-full items-center justify-between">
|
||
<div className="flex items-center gap-2">
|
||
<p className="text-B14 font-bold">مقدار تخفیف :</p>
|
||
<div className="bg-BLACK rounded-full text-WHITE text-R14 px-2 py-1">
|
||
{discountPercent}%
|
||
</div>
|
||
</div>
|
||
<p className="text-B14 font-bold">
|
||
{formatPrice(
|
||
(
|
||
Number(item.productVariant?.price) -
|
||
Number(item.productVariant?.discountPrice)
|
||
).toString()
|
||
)}{" "}
|
||
تومان
|
||
</p>
|
||
</div>
|
||
) : null}
|
||
|
||
{item?.productVariant?.stock && item?.productVariant?.stock > 0 ? (
|
||
<div className="flex w-full items-center justify-between">
|
||
<div className="flex items-center gap-2"></div>
|
||
<QuantityControl
|
||
isPending={isLoading}
|
||
quantity={item.quantity}
|
||
maxQuantity={item.productVariant?.stock || 0}
|
||
onIncrease={() =>
|
||
onQuantityChange(item.id || "", item.quantity + 1)
|
||
}
|
||
onDecrease={() =>
|
||
onQuantityChange(item.id || "", item.quantity - 1)
|
||
}
|
||
onRemove={() => onRemoveItem(item.id || "")}
|
||
/>
|
||
</div>
|
||
) : (
|
||
<div className="flex w-full items-center justify-between">
|
||
<p className="text-B14 font-bold">محصول در حال حاضر موجود نیست</p>
|
||
<Button
|
||
variant="dark"
|
||
className="flex items-center gap-2 bg-red-500"
|
||
size="sm"
|
||
onClick={() => onRemoveItem(item.id || "")}
|
||
>
|
||
<Trash size={16} />
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
});
|
||
|
||
// Color Selector Component
|
||
const ColorSelector = ({ color }: { color: string }) => (
|
||
<>
|
||
{color && (
|
||
<div className="flex w-full items-center justify-between">
|
||
<p className="text-B14 font-bold">رنگ :</p>
|
||
<div
|
||
className="w-10 h-10 rounded-[7px] flex items-center justify-center border border-BLACK"
|
||
style={{
|
||
backgroundColor: color,
|
||
}}
|
||
>
|
||
<Check size={24} className="text-WHITE" />
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|
||
|
||
// Size Selector Component
|
||
const SizeSelector = ({ size }: { size: string }) => (
|
||
<>
|
||
{size && (
|
||
<div className="flex w-full items-center justify-between">
|
||
<p className="text-B14 font-bold">سایز :</p>
|
||
<p className="text-BLACK text-R12">{size}</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|