305 lines
10 KiB
JavaScript
305 lines
10 KiB
JavaScript
"use client";
|
|
import React, { useContext, useEffect, useState } from "react";
|
|
import { Trash2, Plus } from "lucide-react";
|
|
import authAxios, { backendUrl } from "@/utils/axios";
|
|
import ProductContext from "@/app/contexts/productContext";
|
|
import EmptyCart from "./emptyCart";
|
|
import { PayPalScriptProvider, PayPalButtons } from "@paypal/react-paypal-js";
|
|
import axios from "axios";
|
|
import { useRouter } from "next/navigation";
|
|
import PaymentComponent from "../payment/paymentComponent";
|
|
import { useCurrency } from "@/app/contexts/currencyContext";
|
|
import Link from "next/link";
|
|
import Image from "next/image";
|
|
import toast from "react-hot-toast";
|
|
|
|
const ShoppingCart = () => {
|
|
const [cartItems, setCartItems] = useState(null);
|
|
const [error, setError] = useState(null);
|
|
const [shippingAddress, setShippingAddress] = useState(""); // New state for address
|
|
const router = useRouter();
|
|
const { cartFn } = useContext(ProductContext);
|
|
const { formatPrice } = useCurrency();
|
|
|
|
const getCart = async () => {
|
|
const token = localStorage.getItem("token");
|
|
|
|
if (!token) {
|
|
console.log("User not logged in, setting empty cart");
|
|
setCartItems([]);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await authAxios.get("/orders/cart/");
|
|
setCartItems(response.data);
|
|
} catch (error) {
|
|
console.error("Error fetching cart:", error);
|
|
setCartItems([]);
|
|
}
|
|
};
|
|
useEffect(() => {
|
|
getCart();
|
|
}, []);
|
|
|
|
|
|
const handleQuantityChange = async (variantId, designId, quantityChange) => {
|
|
const response = await cartFn(variantId, designId, quantityChange);
|
|
console.log(response)
|
|
if (response?.status == 200 || response?.status == 204) {
|
|
setCartItems((prev) => {
|
|
if (!prev) return prev;
|
|
|
|
const updatedItems = prev[0].items.map((item) => {
|
|
if (item.variant.id === variantId && item?.design?.id === designId) {
|
|
const updatedQuantity = item.quantity + quantityChange;
|
|
const updatedTotalPrice =
|
|
(item.variant.price + (item?.design?.design_price || 0)) *
|
|
updatedQuantity;
|
|
|
|
if (updatedQuantity <= 0) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
...item,
|
|
quantity: updatedQuantity,
|
|
total_price: updatedTotalPrice,
|
|
};
|
|
}
|
|
return item;
|
|
});
|
|
|
|
const filteredItems = updatedItems.filter((item) => item !== null);
|
|
|
|
return [
|
|
{
|
|
...prev[0],
|
|
items: filteredItems,
|
|
total_amount: filteredItems.reduce(
|
|
(sum, item) => sum + item.total_price,
|
|
0
|
|
),
|
|
},
|
|
];
|
|
});
|
|
}
|
|
};
|
|
|
|
if (!cartItems) {
|
|
return null;
|
|
}
|
|
|
|
if (!cartItems[0]?.items?.length) {
|
|
return <EmptyCart />;
|
|
}
|
|
|
|
const handleRazorpayPaymentSuccess = async (response) => {
|
|
console.log("Razorpay Payment Successful", response);
|
|
|
|
try {
|
|
const paymentData = {
|
|
paymentId: response.razorpay_payment_id,
|
|
orderId: response.razorpay_order_id,
|
|
signature: response.razorpay_signature,
|
|
cartId: cartItems[0].id,
|
|
address: response.address,
|
|
};
|
|
|
|
const apiResponse = await authAxios.post("/orders/payment/", paymentData);
|
|
router.push("/accounts/profile/orders");
|
|
toast.success("Your Order Is Successful!");
|
|
console.log(apiResponse);
|
|
} catch (error) {
|
|
console.error("Error processing Razorpay payment:", error);
|
|
toast.error("Payment processing failed. Please try again.");
|
|
}
|
|
};
|
|
|
|
const handlePayPalPaymentSuccess = async (order) => {
|
|
console.log("PayPal Payment Successful", order);
|
|
|
|
try {
|
|
const paymentData = {
|
|
paymentId: order.id,
|
|
payerEmail: order.payer.email_address,
|
|
cartId: cartItems[0].id,
|
|
address: order.address,
|
|
};
|
|
|
|
const apiResponse = await authAxios.post("/orders/payment/", paymentData);
|
|
router.push("/accounts/profile/orders");
|
|
toast.success("Your Order Is Successful!");
|
|
console.log(apiResponse);
|
|
} catch (error) {
|
|
console.error("Error processing PayPal payment:", error);
|
|
toast.error("Payment processing failed. Please try again.");
|
|
}
|
|
};
|
|
|
|
if (!cartItems) {
|
|
return null;
|
|
}
|
|
|
|
if (!cartItems[0]?.items?.length) {
|
|
return <EmptyCart />;
|
|
}
|
|
|
|
return (
|
|
<div className="w-full bg-white">
|
|
<div className="w-full px-4 md:px-8 max-w-[1600px] mx-auto font-['Inter']">
|
|
<h1 className="text-center text-2xl font-medium mb-12 tracking-wide">
|
|
SHOPPING CART
|
|
</h1>
|
|
|
|
<div className="space-y-6 relative">
|
|
<div className="hidden lg:grid lg:grid-cols-[1fr,auto,120px,auto,50px] lg:gap-8 text-sm text-gray-500 mb-4 pr-2">
|
|
<div />
|
|
<div className="w-[140px] text-right">Price</div>
|
|
<div className="text-center">Quantity</div>
|
|
<div className="w-[140px] text-right">Total</div>
|
|
<div />
|
|
</div>
|
|
|
|
{cartItems[0]?.items?.map((item) => (
|
|
<div className="relative" key={item.id}>
|
|
<div className="flex flex-col lg:flex-row lg:items-center">
|
|
<div className="flex items-start gap-4 mb-4 lg:mb-0 lg:flex-1">
|
|
<Image
|
|
src={item.variant?.product?.images[0]?.image}
|
|
alt="Siddha Mala"
|
|
width={80}
|
|
height={80}
|
|
className="object-cover bg-gray-100"
|
|
/>
|
|
<div className="space-y-1">
|
|
<h3 className="font-medium">
|
|
{item.variant.product.product_name}
|
|
</h3>
|
|
{item.variant.size?.size_name && (
|
|
<p className="text-sm text-gray-600">
|
|
Size: {item.variant.size.size_name}
|
|
</p>
|
|
)}
|
|
{item?.design?.design_name && (
|
|
<p className="text-sm text-gray-600">
|
|
Your Design: {item.design.design_name}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-1 lg:grid-cols-[140px,120px,140px,50px] lg:gap-8 items-center">
|
|
<span className="text-gray-900 text-right">
|
|
{formatPrice(
|
|
item.variant.price + (item.design?.design_price || 0)
|
|
)}
|
|
</span>
|
|
<div className="flex items-center bg-gray-50 px-2 justify-center">
|
|
{item.quantity > 1 && (
|
|
<button
|
|
onClick={() =>
|
|
handleQuantityChange(
|
|
item.variant.id,
|
|
item?.design?.id,
|
|
-1
|
|
)
|
|
}
|
|
className="px-2 py-2 text-[#c19a5b] hover:text-[#ab885b]"
|
|
>
|
|
-
|
|
</button>
|
|
)}
|
|
<span className="px-3 py-2">{item.quantity}</span>
|
|
<button
|
|
onClick={() =>
|
|
handleQuantityChange(
|
|
item.variant.id,
|
|
item?.design?.id,
|
|
+1
|
|
)
|
|
}
|
|
className="px-2 py-2 text-[#c19a5b] hover:text-[#ab885b]"
|
|
>
|
|
+
|
|
</button>
|
|
</div>
|
|
<span className="text-gray-900 text-right">
|
|
{formatPrice(item.total_price)}
|
|
</span>
|
|
<button className="text-[#c19a5b] hover:text-[#ab885b] justify-self-center">
|
|
<Trash2
|
|
size={18}
|
|
onClick={() =>
|
|
handleQuantityChange(
|
|
item.variant.id,
|
|
item.design.id,
|
|
-item.quantity
|
|
)
|
|
}
|
|
/>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="h-px w-full bg-gradient-to-r from-transparent via-gray-200 to-transparent my-6" />
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex flex-col sm:flex-row gap-4 sm:gap-8 mt-8">
|
|
<button className="text-[#c19a5b] hover:text-[#ab885b] font-medium order-2 sm:order-1">
|
|
<Link href="/">← CONTINUE SHOPPING</Link>
|
|
</button>
|
|
<button className="text-[#c19a5b] hover:text-[#ab885b] sm:ml-auto font-medium order-1 sm:order-2">
|
|
CLEAR SHOPPING CART
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex justify-center lg:ml-96 lg:mr-96 mt-10 lg:mt-0">
|
|
<div className="space-y-4 w-full max-w-md mx-auto lg:max-w-none">
|
|
<div className="flex justify-between">
|
|
<span>SUBTOTAL</span>
|
|
<span>{formatPrice(cartItems[0].total_amount)}</span>
|
|
</div>
|
|
<div className="flex justify-between font-medium">
|
|
<span>GRAND TOTAL</span>
|
|
<span className="text-[#c19a5b]">
|
|
{formatPrice(cartItems[0].total_amount)}
|
|
</span>
|
|
</div>
|
|
<label className="flex items-center gap-2">
|
|
<input type="checkbox" className="accent-[#c19a5b]" />
|
|
<span className="text-sm">
|
|
I agree with the{" "}
|
|
<Link
|
|
href="/policies/terms-of-services"
|
|
className="text-blue-600"
|
|
>
|
|
terms of services
|
|
</Link>{" "}
|
|
and{" "}
|
|
<Link href="/policies/refund-policy" className="text-blue-600">
|
|
return And Cancellation policy.
|
|
</Link>
|
|
</span>
|
|
</label>
|
|
|
|
{/* Payment Component for Razorpay and PayPal */}
|
|
<PaymentComponent
|
|
amount={cartItems[0].total_amount}
|
|
onSuccess={(response) => {
|
|
if (response.razorpay_payment_id) {
|
|
handleRazorpayPaymentSuccess(response);
|
|
} else {
|
|
handlePayPalPaymentSuccess(response);
|
|
}
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ShoppingCart;
|