Vitron-Front/app/routes/cart._index.tsx
2026-04-29 01:44:16 +03:30

218 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 },
];
};