There are two issues, discount_amount doesn't change when changing the products qty...
also if product qty is larger than selectedSize.qty  don't allow to add more...
And if selected.size.qty is less than 1, then show out of stock.
Cart.jsx:
'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 EmptyCart from "@/components/EmptyCart";
import { CircleArrowLeft } from "lucide-react";
import { useNotification } from '@/components/NotificationContext';
import "./NotificationStyles.css"
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 [isApplyingCoupon, setIsApplyingCoupon] = useState(false);
    const [showConfirmation, setShowConfirmation] = useState(false);
    const [confirmationMessage, setConfirmationMessage] = useState('');
    const [couponMessage, setCouponMessage] = useState('');
    const [appliedCoupon, setAppliedCoupon] = useState('');
    const [isOrderSet, setIsOrderSet] = useState(false);
    const [confirmationAction, setConfirmationAction] = useState(null);

    const router = useRouter();
    const { addNotification } = useNotification();

    // Fetch cart items from localStorage and enrich with product details
    const fetchCartItems = useCallback(async () => {
        try {
            const storedCart = JSON.parse(localStorage.getItem('cart')) || [];
            if (storedCart.length === 0) {
                setIsLoading(false);
                return;
            }

            const uniqueProductIds = [...new Set(storedCart.map(item => item.product_id))];
            const productDetailsMap = {};

            // Fetch all unique products in parallel
            await Promise.all(uniqueProductIds.map(async (productId) => {
                try {
                    const response = await fetch(`https://api.naayiq.com/products/${productId}`);
                    if (!response.ok) {
                        throw new Error(`Failed to fetch product ${productId}: ${response.statusText}`);
                    }
                    const data = await response.json();
                    productDetailsMap[productId] = data.product;
                } catch (error) {
                    addNotification('error', `Error fetching product ${productId}. It will be skipped.`);
                }
            }));

            // Enrich cart items with product details
            const itemsWithDetails = storedCart.map(item => {
                const product = productDetailsMap[item.product_id];
                if (!product) {
                    return null; // Skip items with failed fetches
                }

                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: selectedSize && selectedSize.images.length > 0
                        ? `https://storage.naayiq.com/resources/${selectedSize.images[0]}`
                        : '/placeholder-image.png', // Fallback image
                    price: parseInt(selectedSize?.price || product.price, 10),
                    color: selectedColor?.name || 'N/A',
                    size: selectedSize?.name || 'N/A',
                };
            }).filter(item => item !== null); // Remove items with failed fetches

            setCartItems(itemsWithDetails);
        } catch (error) {
            addNotification('error', 'Failed to load cart items.');
        } finally {
            setIsLoading(false);
        }
    }, [addNotification]);

    useEffect(() => {
        if (typeof window !== 'undefined') {
            fetchCartItems();
        }
    }, [fetchCartItems]);

    // Calculate subtotal whenever cartItems change
    useEffect(() => {
        const total = cartItems.reduce((sum, item) => sum + item.price * (item.qty || 1), 0);
        setSubTotal(total);
    }, [cartItems]);

    // Handle quantity updates
    const handleUpdateQuantity = useCallback((id, newQty) => {
        setCartItems(prevItems => {
            const updatedItems = prevItems.map(item =>
                item.product_id === id ? { ...item, qty: newQty } : item
            );
            try {
                localStorage.setItem('cart', JSON.stringify(updatedItems));
            } catch (error) {
                addNotification('error', 'Failed to update cart in localStorage.');
            }
            return updatedItems;
        });
    }, [addNotification]);

    // Handle item removal with confirmation
    const handleRemoveItem = useCallback((id) => {
        setConfirmationMessage('Are you sure you want to remove this item from your cart?');
        setConfirmationAction(() => () => {
            setCartItems(prevItems => {
                const updatedItems = prevItems.filter(item => item.product_id !== id);
                try {
                    localStorage.setItem('cart', JSON.stringify(updatedItems));
                } catch (error) {
                    addNotification('error', 'Failed to update cart in localStorage.');
                }
                return updatedItems;
            });
            setShowConfirmation(false);
        });
        setShowConfirmation(true);
    }, [addNotification]);

    // Calculate discount based on coupon details
    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;
    }, []);

    // Handle applying and removing coupons
    const handleApplyCoupon = useCallback(async () => {
        if (appliedCoupon) {
            // Removing the applied coupon
            setConfirmationMessage('Are you sure you want to remove the applied coupon?');
            setConfirmationAction(() => () => {
                setAppliedCoupon(null);
                setAppliedCouponId(null);
                setDiscount(0);
                setCoupon('');
                setCouponMessage('');
                setShowConfirmation(false);
                addNotification('success', 'Coupon removed successfully.');
            });
            setShowConfirmation(true);
        } else {
            if (!coupon.trim()) {
                addNotification('error', 'Please enter a coupon code.');
                return;
            }
            // Applying a new coupon
            setIsApplyingCoupon(true);
            try {
                const response = await fetch(`https://api.naayiq.com/coupons/${encodeURIComponent(coupon.trim())}/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.code);
                    setAppliedCouponId(data.coupon.id);
                    setCouponMessage(data.message || 'Coupon applied successfully!');
                } else {
                    setCouponMessage(data.message || "Invalid coupon code");
                }
            } catch (error) {
                console.log(error)
                setCouponMessage("Error applying coupon. Please try again.");
            } finally {
                setIsApplyingCoupon(false);
            }
        }
    }, [appliedCoupon, coupon, subTotal, calculateDiscount, addNotification]);

    // Recalculate discount when subtotal changes
    useEffect(() => {
        if (appliedCouponId) {
            // Optionally refetch coupon details if necessary
            // For now, we assume discount remains the same or is recalculated
            // Here, we recalculate the discount
            // You might need to store coupon details to recalculate
            // For simplicity, we'll leave it as is
        }
    }, [subTotal, appliedCouponId]);

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

    // Calculate per-item discounts for display
    const cartItemsWithDiscount = useMemo(() => {
        if (discount <= 0) {
            return cartItems.map(item => ({
                ...item,
                originalTotal: item.price * (item.qty || 1),
                discountedTotal: item.price * (item.qty || 1),
            }));
        }

        return cartItems.map(item => {
            const itemTotal = item.price * (item.qty || 1);
            const itemShare = subTotal ? itemTotal / subTotal : 0;
            const itemDiscount = Math.round(discount * itemShare);
            const discountedTotal = itemTotal - itemDiscount;
            return {
                ...item,
                originalTotal: itemTotal,
                discountedTotal: discountedTotal,
            };
        });
    }, [cartItems, discount, subTotal]);

    // Handle proceeding to order
    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 },
        };

        try {
            localStorage.setItem('orderData', JSON.stringify(orderData));
            setIsOrderSet(true);
        } catch (error) {
            addNotification('error', 'Failed to set order data. Please try again.');
        }
    }, [appliedCouponId, cartItems, delivery, discount, subTotal, addNotification]);

    // Navigate to order page once order data is set
    useEffect(() => {
        if (isOrderSet) {
            router.push('/cart/order');
            setIsOrderSet(false);
        }
    }, [isOrderSet, router]);

    // Handle confirmation modal actions
    const handleConfirm = useCallback(() => {
        if (confirmationAction) {
            confirmationAction();
            setConfirmationAction(null);
        }
    }, [confirmationAction]);

    const handleCancelConfirmation = useCallback(() => {
        setShowConfirmation(false);
        setConfirmationAction(null);
    }, []);

    if (isLoading) {
        return <Loading />;
    }

    return (
        cartItems.length === 0 ? <EmptyCart /> :
            <>
                <header className="flex items-center mb-6">
                    <button
                        onClick={() => router.back()}
                        className="p-2 relative z-20 cursor-pointer"
                        aria-label="Go back"
                    >
                        <CircleArrowLeft size={52} strokeWidth={0.7} />
                    </button>
                    <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={handleCancelConfirmation}
                                            className="bg-gray-300 text-black px-4 py-2 rounded mr-2"
                                            aria-label="Cancel confirmation"
                                        >
                                            Cancel
                                        </button>
                                        <button
                                            onClick={handleConfirm}
                                            className="bg-red-500 text-white px-4 py-2 rounded"
                                            aria-label="Confirm action"
                                        >
                                            Confirm
                                        </button>
                                    </div>
                                </motion.div>
                            </motion.div>
                        )}
                    </AnimatePresence>

                    <AnimatePresence>
                        {cartItemsWithDiscount.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 || 1}
                                    originalPrice={item.originalTotal}
                                    discountedPrice={item.discountedTotal}
                                    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-4">
                            <span>Discounted Coupon</span>
                            <div className="flex justify-between items-center mt-2">
                                <input
                                    type="text"
                                    value={coupon}
                                    onChange={(e) => setCoupon(e.target.value)}
                                    className={`border rounded w-full p-2 mr-2 ${appliedCoupon ? 'bg-gray-200 cursor-not-allowed' : ''}`}
                                    placeholder="Enter coupon"
                                    disabled={Boolean(appliedCoupon)}
                                    aria-label="Enter coupon code"
                                />
                                <motion.button
                                    whileHover={{ scale: 1.05 }}
                                    whileTap={{ scale: 0.95 }}
                                    onClick={handleApplyCoupon}
                                    className={`${appliedCoupon ? 'bg-red-500' : 'bg-[#3B5345]'} text-white px-4 py-2 rounded`}
                                    disabled={isApplyingCoupon}
                                    aria-label={appliedCoupon ? 'Remove applied coupon' : 'Apply coupon'}
                                >
                                    {isApplyingCoupon
                                        ? 'Applying...'
                                        : (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-2 ${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.toLocaleString()} IQD</span>
                        </div>
                        <div className="flex justify-between mb-2">
                            <span>Discount</span>
                            <span>{discount > 0 ? "-" : ""}{discount.toLocaleString()} IQD</span>
                        </div>
                        <div className="flex justify-between font-bold mt-4">
                            <span>Total Price</span>
                            <span>{totalPrice.toLocaleString()} 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 ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
                        aria-label="Proceed to order"
                    >
                        {isOrderSet ? "Processing..." : "Proceed"}
                    </button>
                </motion.div>
            </>
    );

};

export default Cart;

CartItem.jsx:
import React, { useCallback } from 'react';
import Image from 'next/image';
import { ChevronDown, Plus, Minus } from 'lucide-react';

const CartItem = React.memo(({ id, title, color, image, size, qty, originalPrice, discountedPrice, onUpdateQuantity, onRemove }) => {
    const handleIncrement = useCallback(() => {
        onUpdateQuantity(id, qty + 1);
    }, [id, qty, onUpdateQuantity]);

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

    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}
                    sizes="(max-width: 768px) 100vw, 50vw"
                    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 !== 'N/A' && <p className="text-sm text-center">Color: {color}</p>}
                {size !== 'N/A' && <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>
                {/* Price Section */}
                <div className="text-center">
                    {discountedPrice < originalPrice ? (
                        <>
                            <span className="line-through text-gray-500 text-sm">{originalPrice.toLocaleString()} IQD</span>
                            <br />
                            <span className="text-green-600 font-semibold text-sm">{discountedPrice.toLocaleString()} IQD</span>
                        </>
                    ) : (
                        <span className="font-semibold text-sm">{originalPrice.toLocaleString()} IQD</span>
                    )}
                </div>
            </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"
                    aria-label={`Increase quantity of ${title}`}
                >
                    <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"
                    aria-label={`Decrease quantity of ${title}`}
                >
                    <Minus className="w-6 h-6 text-[#695C5C]" />
                </button>
            </div>
        </div>
    );
});

export default CartItem;