From e64045b0e14c5041b0686ecdd4839d213c8d0133 Mon Sep 17 00:00:00 2001 From: Dwindi Ramadhana Date: Fri, 26 Dec 2025 23:07:18 +0700 Subject: [PATCH] feat: Guest wishlist localStorage + visual state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guest Wishlist Implementation: Problem: Guests couldn't persist wishlist, no visual feedback on wishlisted items Solution: Implemented localStorage-based guest wishlist system Changes: 1. localStorage Storage: - Key: 'woonoow_guest_wishlist' - Stores array of product IDs - Persists across browser sessions - Loads on mount for guests 2. Dual Mode Logic: - Guest (not logged in): localStorage only - Logged in: API + database - isInWishlist() works for both modes 3. Visual State: - productIds Set tracks wishlisted items - Heart icons show filled state when in wishlist - Works in ProductCard, Product page, etc. Result: ✅ Guests can add/remove items (persists in browser) ✅ Heart icons show filled state for wishlisted items ✅ No login required when guest wishlist enabled ✅ Seamless experience for both guests and logged-in users Files Modified: - customer-spa/src/hooks/useWishlist.ts (localStorage implementation) - customer-spa/dist/app.js (rebuilt) Note: Categories/Tags/Attributes pages already exist as placeholder pages --- customer-spa/src/hooks/useWishlist.ts | 58 +++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/customer-spa/src/hooks/useWishlist.ts b/customer-spa/src/hooks/useWishlist.ts index d78d9e8..6a3e524 100644 --- a/customer-spa/src/hooks/useWishlist.ts +++ b/customer-spa/src/hooks/useWishlist.ts @@ -16,6 +16,8 @@ interface WishlistItem { added_at: string; } +const GUEST_WISHLIST_KEY = 'woonoow_guest_wishlist'; + export function useWishlist() { const [items, setItems] = useState([]); const [isLoading, setIsLoading] = useState(false); @@ -26,10 +28,36 @@ export function useWishlist() { const isEnabled = settings?.wishlist_enabled !== false; const isLoggedIn = (window as any).woonoowCustomer?.user?.isLoggedIn; + // Load guest wishlist from localStorage + const loadGuestWishlist = useCallback(() => { + try { + const stored = localStorage.getItem(GUEST_WISHLIST_KEY); + if (stored) { + const guestIds = JSON.parse(stored) as number[]; + setProductIds(new Set(guestIds)); + } + } catch (error) { + console.error('Failed to load guest wishlist:', error); + } + }, []); + + // Save guest wishlist to localStorage + const saveGuestWishlist = useCallback((ids: Set) => { + try { + localStorage.setItem(GUEST_WISHLIST_KEY, JSON.stringify(Array.from(ids))); + } catch (error) { + console.error('Failed to save guest wishlist:', error); + } + }, []); + // Load wishlist on mount useEffect(() => { - if (isEnabled && isLoggedIn) { - loadWishlist(); + if (isEnabled) { + if (isLoggedIn) { + loadWishlist(); + } else { + loadGuestWishlist(); + } } }, [isEnabled, isLoggedIn]); @@ -49,6 +77,17 @@ export function useWishlist() { }, [isLoggedIn]); const addToWishlist = useCallback(async (productId: number) => { + // Guest mode: store in localStorage only + if (!isLoggedIn) { + const newIds = new Set(productIds); + newIds.add(productId); + setProductIds(newIds); + saveGuestWishlist(newIds); + toast.success('Added to wishlist'); + return true; + } + + // Logged in: use API try { await api.post('/account/wishlist', { product_id: productId }); await loadWishlist(); // Reload to get full product details @@ -59,9 +98,20 @@ export function useWishlist() { toast.error(message); return false; } - }, [loadWishlist]); + }, [isLoggedIn, productIds, loadWishlist, saveGuestWishlist]); const removeFromWishlist = useCallback(async (productId: number) => { + // Guest mode: remove from localStorage only + if (!isLoggedIn) { + const newIds = new Set(productIds); + newIds.delete(productId); + setProductIds(newIds); + saveGuestWishlist(newIds); + toast.success('Removed from wishlist'); + return true; + } + + // Logged in: use API try { await api.delete(`/account/wishlist/${productId}`); setItems(items.filter(item => item.product_id !== productId)); @@ -76,7 +126,7 @@ export function useWishlist() { toast.error('Failed to remove from wishlist'); return false; } - }, [items]); + }, [isLoggedIn, productIds, items, saveGuestWishlist]); const toggleWishlist = useCallback(async (productId: number) => { if (productIds.has(productId)) {