From 1c6b76efb4e02c8cec7d4c27a25f20d846e9d890 Mon Sep 17 00:00:00 2001 From: Dwindi Ramadhana Date: Fri, 26 Dec 2025 23:31:43 +0700 Subject: [PATCH] fix: Guest wishlist now fetches and displays full product details MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Guest wishlist showed only product IDs without any useful information - No product name, image, or price - Product links used ID instead of slug (broken routing) - Completely useless user experience Solution: Fetch full product details from API for guest wishlist - Added useEffect to fetch products by IDs: /shop/products?include=123,456,789 - Display actual product data: name, image, price, stock status - Use product slug for proper navigation: /product/{slug} - Same rich experience as logged-in users Implementation: 1. Added ProductData interface for type safety 2. Added guestProducts state and loadingGuest state 3. Fetch products when guest has wishlist items (productIds.size > 0) 4. Display full product cards with images, names, prices 5. Navigate using slug instead of ID 6. Remove from both localStorage and display list Result: ✅ Guests see full product information (name, image, price) ✅ Product links work correctly (/product/product-slug) ✅ Can remove items from wishlist page ✅ Professional user experience matching logged-in users ✅ No more useless 'Product #123' placeholders Files Modified: - customer-spa/src/pages/Wishlist.tsx (fetch and display logic) - customer-spa/dist/app.js (rebuilt) --- customer-spa/src/pages/Wishlist.tsx | 90 +++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/customer-spa/src/pages/Wishlist.tsx b/customer-spa/src/pages/Wishlist.tsx index 04adc04..7bd2c0e 100644 --- a/customer-spa/src/pages/Wishlist.tsx +++ b/customer-spa/src/pages/Wishlist.tsx @@ -1,10 +1,23 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Trash2, ShoppingCart, Heart } from 'lucide-react'; import { useWishlist } from '@/hooks/useWishlist'; import { useCartStore } from '@/lib/cart/store'; import { Button } from '@/components/ui/button'; import { toast } from 'sonner'; +import { apiClient } from '@/lib/api/client'; + +interface ProductData { + id: number; + name: string; + slug: string; + price: string; + regular_price?: string; + sale_price?: string; + image?: string; + on_sale?: boolean; + stock_status?: string; +} /** * Public Wishlist Page - Accessible to both guests and logged-in users @@ -15,18 +28,39 @@ export default function Wishlist() { const navigate = useNavigate(); const { items, isLoading, isLoggedIn, removeFromWishlist, productIds } = useWishlist(); const { addItem } = useCartStore(); + const [guestProducts, setGuestProducts] = useState([]); + const [loadingGuest, setLoadingGuest] = useState(false); + + // Fetch product details for guest wishlist + useEffect(() => { + const fetchGuestProducts = async () => { + if (!isLoggedIn && productIds.size > 0) { + setLoadingGuest(true); + try { + const ids = Array.from(productIds).join(','); + const response = await apiClient.get(`/shop/products?include=${ids}`); + setGuestProducts(response.products || []); + } catch (error) { + console.error('Failed to fetch guest wishlist products:', error); + } finally { + setLoadingGuest(false); + } + } + }; + fetchGuestProducts(); + }, [isLoggedIn, productIds]); const handleRemove = async (productId: number) => { await removeFromWishlist(productId); + // Remove from guest products list + setGuestProducts(prev => prev.filter(p => p.id !== productId)); }; const handleAddToCart = (productId: number, productName: string) => { - // For guests with localStorage wishlist, we only have IDs - // Navigate to product page for now navigate(`/product/${productId}`); }; - if (isLoading) { + if (isLoading || loadingGuest) { return (
@@ -36,10 +70,9 @@ export default function Wishlist() { ); } - // Guest mode: only have product IDs from localStorage - const guestWishlistIds = !isLoggedIn ? Array.from(productIds) : []; - const hasGuestItems = guestWishlistIds.length > 0; - const hasLoggedInItems = items.length > 0; + // Guest mode: have product details fetched from API + const hasGuestItems = !isLoggedIn && guestProducts.length > 0; + const hasLoggedInItems = isLoggedIn && items.length > 0; if (!hasGuestItems && !hasLoggedInItems) { return ( @@ -68,46 +101,57 @@ export default function Wishlist() {

My Wishlist

- {isLoggedIn ? `${items.length} items` : `${guestWishlistIds.length} items`} + {isLoggedIn ? `${items.length} items` : `${guestProducts.length} items`}

- {/* Guest Mode: Show IDs only with limited functionality */} + {/* Guest Mode: Show full product details */} {!isLoggedIn && hasGuestItems && (

- Guest Wishlist: You have {guestWishlistIds.length} items saved locally. - Login to see full details and sync your wishlist. + Guest Wishlist: You have {guestProducts.length} items saved locally. + Login to sync your wishlist to your account.

)} - {/* Guest Wishlist Items (localStorage only - show IDs) */} + {/* Guest Wishlist Items (with full product details) */} {!isLoggedIn && hasGuestItems && (
- {guestWishlistIds.map((productId: number) => ( -
+ {guestProducts.map((product) => ( +
-
- -
+ {product.image ? ( + {product.name} + ) : ( +
+ +
+ )}
-

Product #{productId}

-

Login to see details

+

{product.name}

+

{product.price}

+ {product.stock_status === 'outofstock' && ( +

Out of stock

+ )}