mirgate the new api to the wishlist cart... it uses token barer instead of cors credenintals.
'use client'
import Image from "next/image";
import React, { useState, useEffect, useOptimistic } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { CircleArrowLeft, X } from "lucide-react";
import ShoppingBag from "@/components/ShoppingBag";
import Loading from "@/components/Loading";

const WishlistItem = ({ id, name, price, imageUrl, onRemove }) => {
const router = useRouter();

const handleShoppingBagClick = () => {
    router.push(`/products/${id}`);
};

return (
    <div className="bg-white rounded-lg p-4 flex items-stretch space-x-4 mb-4">
        <div className="relative w-[30%] min-h-[120px]">
            <Image
                src={imageUrl}
                alt={name}
                fill={true}
                unoptimized={true}
                className="object-cover rounded-md"
            />
        </div>
        <div className="flex-grow flex flex-col justify-center">
            <h3 className="font-serif font-medium text-base text-center">{name}</h3>
        </div>
        <div className="flex flex-col justify-center items-center space-y-2">
            <button onClick={() => onRemove(id)} className="p-1">
                <X size={24} />
            </button>
            <button onClick={handleShoppingBagClick} className="bg-[#3B5345] rounded-full p-2">
                <ShoppingBag width={24} height={24} />
            </button>
        </div>
    </div>
);

};

export default function Wishlist() {
const router = useRouter();
const [wishlistItems, setWishlistItems] = useState([]);
const [isLoading, setIsLoading] = useState(true);

const [optimisticWishlist, addOptimisticWishlistItem] = useOptimistic(
    wishlistItems,
    (state, newItem) => [...state, newItem]
);

const fetchWishlist = async () => {
    try {
        const response = await fetch(`${process.env.NEXT_PUBLIC_API}/wishlist`,
            {
                credentials: "include"
            });
        if (!response.ok) {
            throw new Error('Failed to fetch wishlist');
        }
        const data = await response.json();
        setWishlistItems(data);
    } catch (error) {
        console.error('Error fetching wishlist:', error);
    } finally {
        setIsLoading(false);
    }
};

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

const handleRemoveItem = async (id) => {
    // Optimistically remove the item from the UI
    const updatedItems = optimisticWishlist.filter(item => item.id !== id);
    addOptimisticWishlistItem(updatedItems);

    try {
        const response = await fetch(`${process.env.NEXT_PUBLIC_API}/wishlist/${id}`, {
            method: 'DELETE',
            credentials: "include"
        });
        if (!response.ok) {
            throw new Error('Failed to delete item from wishlist');
        }
        // Update the actual state after successful deletion
        setWishlistItems(updatedItems);
    } catch (error) {
        console.error('Error removing item from wishlist:', error);
        // Revert the optimistic update if the server request fails
        await fetchWishlist();
    }
};

return (
    <>
        <header className="flex items-center mb-6">
            <CircleArrowLeft size={52} strokeWidth={0.7} onClick={router.back} className="p-2 relative z-20" />
            <h1 className="text-3xl z-10 text-[#181717] left-0 right-0 absolute font-sans text-center font-medium">
                Wishlist
            </h1>
        </header>
        <main className="flex-grow flex flex-col items-center">
            {isLoading ? (
                <Loading />
            ) : optimisticWishlist.length === 0 ? (
                <>
                    <div className="relative w-full h-[33vh]">
                        <Image
                            src="https://storage.naayiq.com/resources/empty_wishlist.gif"
                            alt="Empty wishlist"
                            fill
                            unoptimized={true}
                            priority={true}
                            quality={100}
                            className="object-contain"
                        />
                    </div>
                    <section>
                        <p className="font-serif text-3xl text-center mb-2 font-semibold">Wishlist is empty!</p>
                        <p className="font-serif text-lg text-center leading-none">Tab Heart Button to start</p>
                        <p className="font-serif text-lg text-center">saving your favorite item.</p>
                        <Link href={"/"} className="min-h-[56px] mt-4 items-center font-serif px-3 ssm:px-6 bg-[#3B5345] flex justify-center text-white text-xl rounded-lg">
                            Start Browsing
                        </Link>
                    </section>
                </>
            ) : (
                <div className="w-full max-w-md">
                    {optimisticWishlist.map(item => (
                        <WishlistItem
                            key={item.id}
                            id={item.id}
                            name={item.name}
                            price={item.price}
                            imageUrl={`https://storage.naayiq.com/resources/${item.images[0]}`}
                            onRemove={handleRemoveItem}
                        />
                    ))}
                </div>
            )}
        </main>
    </>
);

}
backend:
// controllers/wishlistController.ts
import { Request, Response } from 'express';
import {pool} from "../config/database";

// Fetch all product IDs from the user's wishlist
export const getWishlist = async (req: Request, res: Response) => {
const userId = req.user?.userId;

if (!userId) {
    return res.status(400).json({ message: 'User ID not found in token' });
}

try {
    const query = `
        SELECT p.id
        FROM wishlist w
        JOIN products p ON w.product_id = p.id
        WHERE w.user_id = $1
    `;
    const { rows } = await pool.query(query, [userId]);

    res.status(200).json({ wishlist: rows });
} catch (error) {
    console.error('Error fetching wishlist:', error);
    res.status(500).json({ message: 'Internal server error' });
}

};

// Add a product to the user's wishlist
export const addToWishlist = async (req: Request, res: Response) => {
const userId = req.user?.userId;
const { product_id } = req.body;

if (!userId) {
    return res.status(400).json({ message: 'User ID not found in token' });
}

if (!product_id) {
    return res.status(400).json({ message: 'Product ID is required' });
}

try {
    const query = `
        INSERT INTO wishlist (user_id, product_id)
        VALUES ($1, $2)
        ON CONFLICT (user_id, product_id) DO NOTHING
    `;
    await pool.query(query, [userId, product_id]);

    res.status(201).json({ message: 'Product added to wishlist' });
} catch (error) {
    console.error('Error adding to wishlist:', error);
    res.status(500).json({ message: 'Internal server error' });
}

};

// Remove a product from the user's wishlist
export const removeFromWishlist = async (req: Request, res: Response) => {
const userId = req.user?.userId;
const { product_id } = req.body;

if (!userId) {
    return res.status(400).json({ message: 'User ID not found in token' });
}

if (!product_id) {
    return res.status(400).json({ message: 'Product ID is required' });
}

try {
    const query = `
        DELETE FROM wishlist
        WHERE user_id = $1 AND product_id = $2
    `;
    const result = await pool.query(query, [userId, product_id]);

    if (result.rowCount === 0) {
        return res.status(404).json({ message: 'Product not found in wishlist' });
    }

    res.status(200).json({ message: 'Product removed from wishlist' });
} catch (error) {
    console.error('Error removing from wishlist:', error);
    res.status(500).json({ message: 'Internal server error' });
}

};
example of using the api:
'use client';

import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { usePathname } from 'next/navigation';
import { useInView } from 'react-intersection-observer';
import dynamic from 'next/dynamic';
import ProductItem from './ProductItem';
import ProductLoading from '@/components/ProductLoading';
import NoProductsFound from '@/components/NoProductsFound';
import ProductDetail from '@/components/ProductDetail';
import {NotificationProvider, useNotification} from '@/components/NotificationContext';

const SearchComponent = dynamic(() => import('@/components/SearchComponent'), {
ssr: false,
loading: () =>
});

const ProductModal = dynamic(() => import('@/components/ProductDetailModal'), {
ssr: false
});

export default function ProductList({ initialFilters }) {
const { c, sc, b, title, sortBy } = initialFilters;
const [products, setProducts] = useState([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [filter, setFilter] = useState('');
const [query, setQuery] = useState('');
const [wishlist, setWishlist] = useState([])
const [minPrice, setMinPrice] = useState(0);
const [maxPrice, setMaxPrice] = useState(250000); // Adjust based on your data
const [detail, setDetail] = useState(null);
const [selectedProduct, setSelectedProduct] = useState(null);
const first = useRef(true);
const { ref, inView } = useInView({
threshold: 0,
triggerOnce: false
});
const { addNotification } = useNotification(); // Using Notification Context

const fetchWishlist = useCallback(async () => {
    const token = localStorage.getItem("token");
    if (!token) return;

    try {
        const response = await fetch(`${process.env.NEXT_PUBLIC_API}/wishlist`, {
            headers: {
                'Authorization': `Bearer ${token}`,
            },
        });
        if (response.ok) {
            const w = await response.json();
            console.log('Fetched wishlist:', w.wishlist);
            const wishlistIds = w.wishlist.map(item => item.id);
            setWishlist(wishlistIds);
        }
    } catch (error) {
        console.error('Error fetching wishlist:', error);
    }
}, [wishlist, setWishlist]);

const pathname = usePathname(); // Import and use usePathname

const fetchProducts = useCallback(async (currentPage, searchQuery) => { // Added searchQuery
    setLoading(true);
    setError(null);
    try {
        await fetchWishlist()
        const params = new URLSearchParams({
            page: currentPage,
            itemsPerPage: 15, // Can be adjusted or made dynamic
        });

        if (c) params.append('c', c);
        if (sc) params.append('sc', sc);
        if (b) params.append('b', b);
        if (sortBy) params.append("sortBy", sortBy);
        if (searchQuery) params.append('search', searchQuery); // Use searchQuery
        if (minPrice) params.append('minPrice', minPrice);
        if (maxPrice && maxPrice !== Infinity) params.append('maxPrice', maxPrice);
        if (filter) params.append('filter', filter); // Ensure filter is appended correctly


        const response = await fetch(`${process.env.NEXT_PUBLIC_API}/products?${params.toString()}&${filter}`);
        if (!response.ok) {
            throw new Error('Failed to fetch products');
        }
        const data = await response.json();
        setProducts((prev) => [...prev, ...data.products]);
        setHasMore(data.hasMore);
        setPage((prev) => prev + 1);
        // After the initial fetch, set first.current to false
        if (first.current) {
            first.current = false;
        }
    } catch (err) {
        console.error('Error fetching products:', err);
        setError(err.message || 'An error occurred');
    } finally {
        setLoading(false);
    }
}, [c, sc, b, minPrice, maxPrice, filter]);

// Initial fetch on mount
useEffect(() => {
    fetchProducts(page, query); // Pass current query
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); // Only run on mount

// Fetch more products when in view
useEffect(() => {
    if (inView && hasMore && !loading) {
        fetchProducts(page, query); // Pass current query
    }
}, [inView, hasMore, loading, fetchProducts, page, query]);

// Handle search functionality
const handleSearch = useCallback((newQuery) => {
    setQuery(newQuery);
    setProducts([]);
    setPage(1);
    setHasMore(true);
    first.current = true;
    fetchProducts(1, newQuery); // Pass newQuery
}, [fetchProducts]);

// Handle filter changes
const handleFilterChange = useCallback(() => {
    setProducts([]);
    setPage(1);
    setHasMore(true);
    first.current = true; // Reset first ref when filtering
    fetchProducts(1, query); // Pass current query
}, [fetchProducts, query]);

// Watch for filter changes
useEffect(() => {
    if (!first.current) {
        handleFilterChange();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
}, [filter]);

useEffect(() => {
    if (pathname === '/products') {
        setDetail(null);
    }
}, [pathname]);

const handleProductClick = useCallback((product) => {
    setDetail(product);
    const newURL = `/products/${product.id}`;
    window.history.pushState({ path: newURL }, '', newURL);
}, []);

useEffect(() => {
    const handlePopState = (event) => {
        const currentPath = window.location.pathname;
        if (currentPath === '/products') {
            setDetail(null);
        } else {
        }
    };

    window.addEventListener('popstate', handlePopState);

    return () => {
        window.removeEventListener('popstate', handlePopState);
    };
}, []);

// Handle cart interactions
const handleCartClick = useCallback((product) => {
    setSelectedProduct(product);
}, []);

const handleModalClose = useCallback(() => {
    setSelectedProduct(null);
}, []);

const memoizedProducts = useMemo(() => products.map((product) => ({
    ...product,
    cheapestPrice: getCheapestPrice(product),
})), [products]);

return (
    <>
        {detail ? (
            <NotificationProvider>
                <ProductDetail product={detail} isInWishlist={wishlist.includes(detail.id)} />
            </NotificationProvider>
        ) : (
            <>
                <header className="flex justify-center relative mt-20 mb-4">
                    <h1 className="text-3xl z-10 text-[#181717] font-sans text-center font-medium">
                        {title || 'All Products'}
                    </h1>
                </header>
                <SearchComponent
                    minPrice={minPrice}
                    maxPrice={maxPrice}
                    query={query}
                    setQuery={handleSearch}
                    filterQuery={filter}
                    setFilterQuery={setFilter}
                />
                {error && <div className="text-red-500 text-center">{error}</div>}
                {memoizedProducts.length > 0 ? (
                    <div className="grid grid-cols-2 w-full justify-between gap-4 sm:gap-6 ssm3:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
                        {memoizedProducts.map((product) => (
                            <ProductItem
                                key={product.id}
                                id={product.id}
                                name={product.name}
                                isInWishlist={wishlist.includes(product.id)}
                                product={product}
                                handleClick={() => handleProductClick(product)}
                                price={formatPrice(product.cheapestPrice)}
                                imageUrl={product.images[0]?.url || ''}
                                onCartClick={handleCartClick}
                            />
                        ))}
                    </div>
                ) : (
                    // Only show NoProductsFound if not loading and not the first load
                    !loading && !first.current && <NoProductsFound />
                )}
                {loading && <ProductLoading />}
                {!loading && hasMore && (
                    <div ref={ref} style={{ height: '20px' }}></div>
                )}
            </>
        )}
        {selectedProduct && (
            <NotificationProvider>
                <ProductModal
                    productData={{ product: selectedProduct }}
                    isOpen={!!selectedProduct}
                    onClose={handleModalClose}
                />
            </NotificationProvider>
        )}
    </>
);

}

const getCheapestPrice = (product) => {
const prices = [
product.price && product.price !== '0.00'
? parseFloat(product.price)
: Infinity,
...(product.sizes?.map((size) => parseFloat(size.price)) || []),
...(product.colors?.flatMap((color) => [
parseFloat(color.price),
...(color.sizes?.map((size) => parseFloat(size.price)) || []),
]) || []),
].filter((price) => !isNaN(price) && isFinite(price));

return prices.length > 0 ? Math.min(...prices) : null;

};

const formatPrice = (price) => {
if (price == null || price === Infinity) return 'N/A';
return ${price >= 10000 ? price.toLocaleString() : price} IQD;
};

const SearchComponentSkeleton = () => (




);

I want you to display the name of the product and its price... use getCheapestPrice function to do so...
Keep the design as it is... make it more smooth and stable and compatiable especially for Safari users.

To migrate your API integration from using CORS credentials to Bearer tokens and enhance the Wishlist component by displaying product names and their cheapest prices, follow the steps below. Additionally, we'll ensure the design remains smooth, stable, and compatible, especially for Safari users.
1. Backend Adjustments (Optional but Recommended)

Before diving into the frontend changes, it's beneficial to ensure that your backend /wishlist endpoint provides comprehensive product details. This modification will simplify data handling on the frontend.

Current getWishlist Endpoint:

// controllers/wishlistController.ts
export const getWishlist = async (req: Request, res: Response) => {
    const userId = req.user?.userId;

    if (!userId) {
        return res.status(400).json({ message: 'User ID not found in token' });
    }

    try {
        const query = `
            SELECT p.id
            FROM wishlist w
            JOIN products p ON w.product_id = p.id
            WHERE w.user_id = $1
        `;
        const { rows } = await pool.query(query, [userId]);

        res.status(200).json({ wishlist: rows });
    } catch (error) {
        console.error('Error fetching wishlist:', error);
        res.status(500).json({ message: 'Internal server error' });
    }
};

Enhanced getWishlist Endpoint:

Modify the SQL query to fetch additional product details such as name, price, and images. This will allow the frontend to display all necessary information without making multiple API calls.

// controllers/wishlistController.ts
export const getWishlist = async (req: Request, res: Response) => {
    const userId = req.user?.userId;

    if (!userId) {
        return res.status(400).json({ message: 'User ID not found in token' });
    }

    try {
        const query = `
            SELECT p.id, p.name, p.price, p.images
            FROM wishlist w
            JOIN products p ON w.product_id = p.id
            WHERE w.user_id = $1
        `;
        const { rows } = await pool.query(query, [userId]);

        res.status(200).json({ wishlist: rows });
    } catch (error) {
        console.error('Error fetching wishlist:', error);
        res.status(500).json({ message: 'Internal server error' });
    }
};

However please for each wishlist item, fetch its product id by /products/[id]... and get the information from there...