218 lines
8.1 KiB
TypeScript
218 lines
8.1 KiB
TypeScript
import { Link, useNavigate } from "@remix-run/react";
|
||
import { MetaFunction } from "@remix-run/node";
|
||
import { ArrowLeft, Trash2 } from "lucide-react";
|
||
import { useToast } from "../hooks/use-toast";
|
||
import { useGetAllCarts, useClearCart } from "../requestHandler/use-cart-hooks";
|
||
import { formatPrice } from "../utils/helpers";
|
||
import { ProductImageCarousel } from "~/components/product";
|
||
import { CartDetail, CartItem } from "src/api/types";
|
||
import ProfilePagesHeader from "../components/ProfilePagesHeader";
|
||
import UiProvider from "~/components/UiProvider";
|
||
import SellerLogo from "~/components/SellerLogo";
|
||
import { Dialog, DialogContent, DialogOverlay } from "~/components/ui/dialog";
|
||
import { Button } from "~/components/ui/button";
|
||
import { useState } from "react";
|
||
export default function CartIndex() {
|
||
const { data: carts = [], isLoading, refetch, isError } = useGetAllCarts();
|
||
const clearCartMutation = useClearCart();
|
||
const { toast } = useToast();
|
||
const navigate = useNavigate();
|
||
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
||
const [cartToDelete, setCartToDelete] = useState<string | null>(null);
|
||
const calculateCartTotal = (cart: CartDetail) => {
|
||
if (!cart.items || cart.items.length === 0) return "0";
|
||
|
||
const total = cart.items.reduce((sum: number, item: CartItem) => {
|
||
const price = parseInt(item.priceAtPurchase) * item.quantity;
|
||
return sum + price;
|
||
}, 0);
|
||
return total.toString();
|
||
};
|
||
|
||
// Handle clearing a cart
|
||
const handleClearCart = async () => {
|
||
if (!cartToDelete) return;
|
||
|
||
try {
|
||
await clearCartMutation.mutateAsync(cartToDelete);
|
||
|
||
toast({
|
||
title: "سبد خرید",
|
||
description: "سبد خرید با موفقیت خالی شد",
|
||
variant: "default",
|
||
});
|
||
setConfirmDialogOpen(false);
|
||
setCartToDelete(null);
|
||
} catch (error) {
|
||
console.error("Error clearing cart:", error);
|
||
toast({
|
||
title: "خطا",
|
||
description: "خالی کردن سبد خرید با مشکل مواجه شد",
|
||
variant: "destructive",
|
||
});
|
||
}
|
||
};
|
||
|
||
const openConfirmDialog = (cartId: string) => {
|
||
setCartToDelete(cartId);
|
||
setConfirmDialogOpen(true);
|
||
};
|
||
return (
|
||
<div className="bg-background">
|
||
{/* Header */}
|
||
<ProfilePagesHeader hideBackButton={true} title="لیست سبد خرید" />
|
||
|
||
{/* Cart Content */}
|
||
<div className="flex flex-col gap-4 p-4">
|
||
<UiProvider
|
||
isLoading={isLoading}
|
||
isError={isError}
|
||
isEmpty={carts.length === 0}
|
||
onRetry={() => {
|
||
refetch();
|
||
}}
|
||
type="cart"
|
||
>
|
||
<div className="space-y-4 w-full">
|
||
{carts.map((cart) => {
|
||
return (
|
||
<div
|
||
role="button"
|
||
tabIndex={0}
|
||
onKeyDown={() => {}}
|
||
onClick={() => {
|
||
navigate(`/cart/${cart.id}`);
|
||
}}
|
||
key={cart.id}
|
||
className="bg-WHITE overflow-hidden shadow-md rounded-[15px] border flex gap-2"
|
||
>
|
||
<div className="w-2/5">
|
||
<ProductImageCarousel
|
||
images={
|
||
cart.items?.map(
|
||
(item) => item.productVariant?.productImage || ""
|
||
) || []
|
||
}
|
||
hideIndex={true}
|
||
/>
|
||
</div>
|
||
<div className="w-3/5 flex p-4 justify-between gap-2">
|
||
<div className="w-full flex justify-between flex-col gap-3">
|
||
<a
|
||
href={`/seller/${cart.seller?.username}`}
|
||
className="w-full gap-2 flex items-center"
|
||
>
|
||
<SellerLogo
|
||
size="sm"
|
||
src={cart.seller?.logo}
|
||
alt={"seller-image"}
|
||
/>
|
||
|
||
<p className="text-B12 font-bold">
|
||
{cart.seller?.name}
|
||
</p>
|
||
</a>
|
||
<p className="text-B12 font-bold line-clamp-1">
|
||
{cart.items
|
||
?.map(
|
||
(item) => item.productVariant?.productTitle || ""
|
||
)
|
||
.join(", ")}
|
||
</p>
|
||
<div className="w-full gap-1 flex">
|
||
<p className="text-B12 font-bold">مبلغ فاکتور:</p>
|
||
<p className="text-R12">
|
||
{formatPrice(calculateCartTotal(cart))} تومان
|
||
</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex justify-between flex-col gap-3">
|
||
<Trash2
|
||
className={`h-6 w-6 ${
|
||
clearCartMutation.isPending ? "text-GRAY" : "text-RED"
|
||
}`}
|
||
onClick={(e) => {
|
||
e.stopPropagation();
|
||
if (!clearCartMutation.isPending) {
|
||
openConfirmDialog(cart.id || "");
|
||
}
|
||
}}
|
||
/>
|
||
<Link to={`/cart/${cart.id}`}>
|
||
<ArrowLeft className="h-6 w-6" />
|
||
</Link>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
})}
|
||
</div>
|
||
</UiProvider>
|
||
</div>
|
||
|
||
<Dialog open={confirmDialogOpen} onOpenChange={setConfirmDialogOpen}>
|
||
<DialogOverlay />
|
||
<DialogContent className="p-4 bg-WHITE rounded-[15px] w-[calc(100%-40px)]">
|
||
<div className="flex flex-col gap-10">
|
||
<div className={"gap-3 items-center w-full"}>
|
||
<p className="text-B12 font-bold mt-1">تایید حذف سبد خرید</p>
|
||
<p className="text-R12">
|
||
آیا مطمئن هستید که میخواهید این سبد خرید را خالی کنید؟ این عمل
|
||
قابل بازگشت نیست.
|
||
</p>
|
||
</div>
|
||
<div className="flex flex-row gap-6 w-full">
|
||
<Button
|
||
className="w-[100%]"
|
||
size={"sm"}
|
||
variant="dark"
|
||
onClick={() => setConfirmDialogOpen(false)}
|
||
disabled={clearCartMutation.isPending}
|
||
>
|
||
انصراف
|
||
</Button>
|
||
<Button
|
||
onClick={handleClearCart}
|
||
disabled={clearCartMutation.isPending}
|
||
className="w-[100%]"
|
||
size={"sm"}
|
||
variant="primary"
|
||
>
|
||
{clearCartMutation.isPending ? "در حال حذف..." : "حذف سبد"}
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</DialogContent>
|
||
</Dialog>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export const meta: MetaFunction = () => {
|
||
const title = "سبد خرید | ویترون";
|
||
const description =
|
||
"مشاهده و مدیریت سبد خرید شما در ویترون. بررسی محصولات، تغییر تعداد و نهایی کردن خرید.";
|
||
const imageUrl =
|
||
"https://vitrownstatics.s3.ir-thr-at1.arvanstorage.ir/SVG-06.svg?versionId=";
|
||
|
||
return [
|
||
{ title },
|
||
{ name: "description", content: description },
|
||
{ name: "keywords", content: "سبد خرید، خرید آنلاین، پرداخت، ویترون" },
|
||
{ name: "robots", content: "noindex, follow" },
|
||
|
||
// Open Graph
|
||
{ property: "og:type", content: "website" },
|
||
{ property: "og:title", content: title },
|
||
{ property: "og:description", content: description },
|
||
{ property: "og:image", content: imageUrl },
|
||
{ property: "og:site_name", content: "ویترون" },
|
||
{ property: "og:locale", content: "fa_IR" },
|
||
|
||
// Twitter Card
|
||
{ name: "twitter:card", content: "summary" },
|
||
{ name: "twitter:title", content: title },
|
||
{ name: "twitter:description", content: description },
|
||
];
|
||
};
|