When a coupon is applied, its discount amount whatever it is percentage or amount should be divided to the cart items, each cart item price should have line through in its price... and then under it show the new price (make this in a neat design) however keep the other design as it is... Just another thing when applying coupon show Validating or Applying whatever you see it as good message instead of Apply... till it is finished...
improve the performance and stability if possible (but don't change anything from the design or structure except the line through part...)
'use client';
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import CartItem from './CartItem';
import { motion, AnimatePresence } from 'framer-motion';
import { useRouter } from "next/navigation";
import Loading from "@/components/Loading";
import { useAppDispatch, useAppSelector } from "@/lib/hook";
import { setOrder } from "@/lib/features/orderSlice";
import EmptyCart from "@/components/EmptyCart";
import { CircleArrowLeft } from "lucide-react";

const Cart = () => {
    const [cartItems, setCartItems] = useState([]);
    const [subTotal, setSubTotal] = useState(0);
    const [delivery] = useState(5000);
    const [discount, setDiscount] = useState(0);
    const [coupon, setCoupon] = useState('');
    const [appliedCouponId, setAppliedCouponId] = useState(null);
    const [isLoading, setIsLoading] = useState(true);
    const [showConfirmation, setShowConfirmation] = useState(false);
    const [confirmationMessage, setConfirmationMessage] = useState('');
    const [couponMessage, setCouponMessage] = useState('');
    const [appliedCoupon, setAppliedCoupon] = useState('');
    const [isOrderSet, setIsOrderSet] = useState(false);
    const dispatch = useAppDispatch();
    const order = useAppSelector(state => state.order);
    const router = useRouter();

    const fetchCartItems = useCallback(async () => {
        console.log('Fetching cart items...');
        const storedCart = JSON.parse(localStorage.getItem('cart')) || [];
        console.log('Stored cart:', storedCart);
        if (storedCart.length === 0) {
            console.log('Cart is empty.');
            setIsLoading(false);
            return;
        }

        try {
            const itemsWithDetails = await Promise.all(
                storedCart.map(async (item) => {
                    try {
                        const response = await fetch(`https://api.naayiq.com/products/${item.product_id}`);
                        console.log(`Fetching product ${item.product_id}:`, response.status);
                        if (!response.ok) {
                            throw new Error(`Failed to fetch product ${item.product_id}: ${response.statusText}`);
                        }
                        const data = await response.json();
                        const product = data.product;
                        const selectedSize = product.sizes.find(size => size.id === item.size_id);
                        const selectedColor = product.colors.find(color => color.id === item.color_id);

                        return {
                            ...item,
                            title: product.name,
                            image: `https://storage.naayiq.com/resources/${selectedSize?.images[0]}`,
                            price: parseInt(selectedSize?.price || product.price),
                            color: selectedColor?.name,
                            size: selectedSize?.name,
                        };
                    } catch (error) {
                        console.error(`Error fetching product ${item.product_id}:`, error);
                        return item; // Return the original item if fetching fails
                    }
                })
            );
            console.log('Fetched cart items with details:', itemsWithDetails);
            setCartItems(itemsWithDetails);
        } catch (error) {
            console.error('Error in fetchCartItems:', error);
        } finally {
            console.log('Setting isLoading to false.');
            setIsLoading(false);
        }
    }, []);

    useEffect(() => {
        fetchCartItems();
    }, [fetchCartItems]);

    useEffect(() => {
        const total = cartItems.reduce((sum, item) => sum + item.price * (item.qty || 1), 0);
        console.log('Calculating subTotal:', total);
        setSubTotal(total);
    }, [cartItems]);

    const handleUpdateQuantity = useCallback((id, newQty) => {
        setCartItems(prevItems => {
            const updatedItems = prevItems.map(item =>
                item.product_id === id ? { ...item, qty: newQty } : item
            );
            localStorage.setItem('cart', JSON.stringify(updatedItems));
            console.log(`Updated quantity for product ${id} to ${newQty}`);
            return updatedItems;
        });
    }, []);

    const handleRemoveItem = useCallback((id) => {
        setShowConfirmation(true);
        setConfirmationMessage('Are you sure you want to remove this item from your cart?');
        setConfirmAction(() => () => {
            setCartItems(prevItems => {
                const updatedItems = prevItems.filter(item => item.product_id !== id);
                localStorage.setItem('cart', JSON.stringify(updatedItems));
                console.log(`Removed product ${id} from cart.`);
                return updatedItems;
            });
            setShowConfirmation(false);
        });
    }, []);

    const calculateDiscount = useCallback((couponDetails, subtotal) => {
        if (couponDetails.discount_percentage) {
            const discountAmount = subtotal * (couponDetails.discount_percentage / 100);
            return Math.min(discountAmount, couponDetails.max_discount_amount || Infinity);
        } else if (couponDetails.discount_amount) {
            return Math.min(couponDetails.discount_amount, subtotal);
        }
        return 0;
    }, []);

    const handleApplyCoupon = useCallback(async () => {
        if (appliedCoupon) {
            setShowConfirmation(true);
            setConfirmationMessage('Are you sure you want to remove the applied coupon?');
            setConfirmAction(() => () => {
                setAppliedCoupon(null);
                setAppliedCouponId(null);
                setDiscount(0);
                setCoupon('');
                setCouponMessage('');
                setShowConfirmation(false);
                console.log('Removed applied coupon.');
            });
        } else {
            console.log(`Applying coupon: ${coupon}`);
            try {
                const response = await fetch(`https://api.naayiq.com/coupons/${encodeURIComponent(coupon)}/activate`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({ price: subTotal })
                });
                const data = await response.json();

                if (response.ok) {
                    const calculatedDiscount = calculateDiscount(data.coupon, subTotal);
                    setDiscount(calculatedDiscount);
                    setAppliedCoupon(data.coupon);
                    setAppliedCouponId(data.coupon.id);
                    setCouponMessage(data.message || 'Coupon applied successfully!');
                    console.log(`Coupon applied: ${data.coupon.id} with discount ${calculatedDiscount}`);
                } else {
                    setCouponMessage(data.message || "Invalid coupon code");
                    console.warn('Error applying coupon:', data.message);
                }
            } catch (error) {
                console.error('Error applying coupon:', error);
                setCouponMessage("Error applying coupon. Please try again.");
            }
        }
    }, [appliedCoupon, coupon, subTotal, calculateDiscount]);

    useEffect(() => {
        if (appliedCoupon) {
            const newDiscount = calculateDiscount(appliedCoupon, subTotal);
            setDiscount(newDiscount);
            console.log(`Recalculated discount based on applied coupon: ${newDiscount}`);
        }
    }, [subTotal, appliedCoupon, calculateDiscount]);

    const totalPrice = useMemo(() => {
        const discountedTotal = subTotal - discount;
        return Math.max(discountedTotal, 0);
    }, [subTotal, discount]);

    const [confirmAction, setConfirmAction] = useState(() => {});

    const handleProceed = useCallback(() => {
        const orderData = {
            coupon_id: appliedCouponId,
            items: cartItems.map(item => ({
                product_id: item.product_id,
                size_id: item.size_id || null,
                color_id: item.color_id || null,
                quantity: item.qty || 1,
            })),
            info: { delivery, discount, subTotal },
        };
        console.log('Storing order data in localStorage:', orderData);

        localStorage.setItem('orderData', JSON.stringify(orderData));
        setIsOrderSet(true)

    }, [appliedCouponId, cartItems, delivery, discount, subTotal, router]);
    useEffect(() => {
        if (isOrderSet) {
            console.log('Order has been set. Navigating to /cart/order');
            router.push('/cart/order');
            setIsOrderSet(false);
        }
    }, [isOrderSet]);

    if (isLoading) {
        console.log('Loading is true. Rendering <Loading /> component.');
        return <Loading />;
    }

    return (
        cartItems.length === 0 ? <EmptyCart /> :
            <>
                <header className="flex items-center mb-6">
                    <CircleArrowLeft size={52} strokeWidth={0.7} onClick={() => router.back()} className="p-2 relative z-20 cursor-pointer" />
                    <h1 className="text-3xl z-10 text-[#181717] left-0 right-0 absolute font-sans text-center font-medium">
                        Cart
                    </h1>
                </header>
                <motion.div
                    initial={{ opacity: 0 }}
                    animate={{ opacity: 1 }}
                    exit={{ opacity: 0 }}
                    className="p-4 overflow-x-hidden font-serif container mx-auto relative"
                >
                    <AnimatePresence>
                        {showConfirmation && (
                            <motion.div
                                initial={{ opacity: 0 }}
                                animate={{ opacity: 1 }}
                                exit={{ opacity: 0 }}
                                className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50"
                            >
                                <motion.div
                                    initial={{ scale: 0.9 }}
                                    animate={{ scale: 1 }}
                                    exit={{ scale: 0.9 }}
                                    className="bg-white p-4 rounded-lg shadow-lg"
                                >
                                    <p className="mb-4">{confirmationMessage}</p>
                                    <div className="flex justify-end">
                                        <button
                                            onClick={() => setShowConfirmation(false)}
                                            className="bg-gray-300 text-black px-4 py-2 rounded mr-2"
                                        >
                                            Cancel
                                        </button>
                                        <button
                                            onClick={confirmAction}
                                            className="bg-red-500 text-white px-4 py-2 rounded"
                                        >
                                            Confirm
                                        </button>
                                    </div>
                                </motion.div>
                            </motion.div>
                        )}
                    </AnimatePresence>

                    <AnimatePresence>
                        {cartItems.map(item => (
                            <motion.div
                                key={item.product_id}
                                initial={{ opacity: 0, y: 20 }}
                                animate={{ opacity: 1, y: 0 }}
                                exit={{ opacity: 0, y: -20 }}
                                transition={{ duration: 0.3 }}
                            >
                                <CartItem
                                    id={item.product_id}
                                    title={item.title}
                                    color={item.color}
                                    image={item.image}
                                    size={item.size}
                                    qty={item.qty}
                                    price={item.price}
                                    onUpdateQuantity={handleUpdateQuantity}
                                    onRemove={handleRemoveItem}
                                />
                            </motion.div>
                        ))}
                    </AnimatePresence>

                    <motion.div
                        initial={{ opacity: 0, y: 20 }}
                        animate={{ opacity: 1, y: 0 }}
                        transition={{ delay: 0.3 }}
                        className="mt-8 bg-white rounded-lg shadow p-4"
                    >
                        <h2 className="text-xl font-sans font-medium mb-4">Price Details</h2>
                        <div className="mb-2">
                            <span>Discounted Coupon</span>
                            <div className="flex justify-between items-center">
                                <input
                                    type="text"
                                    value={coupon}
                                    onChange={(e) => setCoupon(e.target.value)}
                                    className="border rounded w-full p-2 mr-2"
                                    placeholder="Enter coupon"
                                    disabled={Boolean(appliedCoupon)}
                                />
                                <motion.button
                                    whileHover={{ scale: 1.05 }}
                                    whileTap={{ scale: 0.95 }}
                                    onClick={handleApplyCoupon}
                                    className={`${appliedCoupon ? 'bg-red-500' : 'bg-[#3B5345]'} text-white px-8 py-2 rounded`}
                                >
                                    {appliedCoupon ? 'Remove' : 'Apply'}
                                </motion.button>
                            </div>
                            <AnimatePresence>
                                {couponMessage && (
                                    <motion.div
                                        initial={{ opacity: 0, y: -10 }}
                                        animate={{ opacity: 1, y: 0 }}
                                        exit={{ opacity: 0, y: -10 }}
                                        className={`text-sm mt-1 ${appliedCoupon ? 'text-green-500' : 'text-red-500'}`}
                                    >
                                        {couponMessage}
                                    </motion.div>
                                )}
                            </AnimatePresence>
                        </div>
                        <div className="flex justify-between mb-2">
                            <span>Sub-total</span>
                            <span>{subTotal} IQD</span>
                        </div>
                        <div className="flex justify-between mb-2">
                            <span>Discount</span>
                            <span>{discount > 0 ? "-" : ""}{discount} IQD</span>
                        </div>
                        <div className="flex justify-between font-bold mt-4">
                            <span>Total Price</span>
                            <span>{totalPrice} IQD</span>
                        </div>
                    </motion.div>
                    <button
                        onClick={handleProceed}
                        disabled={isOrderSet}
                        className="w-full bg-[#3B5345] text-white py-3 rounded-lg font-medium text-lg mt-6 block text-center"
                    >
                        {isOrderSet ? "Processing..." : "Proceed"}
                    </button>
                </motion.div>
            </>
    );
};

export default Cart;
import React from 'react';
import Image from 'next/image';
import { ChevronDown, Plus, Minus } from 'lucide-react';

const CartItem = ({ id, title, color, image, size, qty, price, onUpdateQuantity, onRemove }) => {
    const handleIncrement = () => {
        onUpdateQuantity(id, qty + 1);
    };

    const handleDecrement = () => {
        if (qty > 1) {
            onUpdateQuantity(id, qty - 1);
        } else {
            onRemove(id);
        }
    };

    return (
        <div className="flex gap-2 justify-between font-serif items-stretch p-4 bg-[rgba(246,243,241,0.3)] shadow-[0px_2px_4px_3px_rgba(0,0,0,0.1)] rounded-md mb-4 w-full">
            <div className="w-full max-h-[300px] relative aspect-[4/5] ssm:w-1/2 bg-transparent rounded-lg overflow-hidden">
                <Image
                    src={image}
                    alt={title}
                    fill={true}
                    unoptimized={true}
                    className="object-cover"
                />
            </div>

            <div className="flex flex-col items-center gap-3 w-[120px]">
                <h3 className="font-semibold text-sm text-center leading-tight">{title}</h3>
                {color ? <p className="text-sm text-center">Color:{color}</p> : ""}
                {size ? <p className="text-sm text-center">Size:{size}</p> : ""}
                <div className="flex items-center justify-between w-[86px] h-6 bg-[#F6F3F1] rounded-lg px-3">
                    <span className="text-sm">Qty:{qty}</span>
                    <ChevronDown size={16} />
                </div>
                <p className="font-semibold text-sm text-center">{price*qty} IQD</p>
            </div>

            <div className="flex flex-col justify-between gap-2">
                <button
                    onClick={handleIncrement}
                    className="w-[41px] h-[40px] flex items-center justify-center bg-[#3B5345] rounded-lg"
                >
                    <Plus className="w-6 h-6 text-white" />
                </button>
                <button
                    onClick={handleDecrement}
                    className="w-[41px] h-[40px] flex items-center justify-center bg-white border border-[rgba(105,92,92,0.5)] rounded-lg"
                >
                    <Minus className="w-6 h-6 text-[#695C5C]" />
                </button>
            </div>
        </div>
    );
};

export default CartItem;