feat: Go-to-Account button + wishlist merge on login
1. ThankYou page - Go to Account button: - Added for logged-in users (next to Continue Shopping) - Shows in both receipt and basic templates - Uses outline variant with User icon 2. Wishlist merge on login: - Reads guest wishlist from localStorage (woonoow_guest_wishlist) - POSTs each product to /account/wishlist API - Handles duplicates gracefully (skips on error) - Clears localStorage after successful merge
This commit is contained in:
@@ -54,6 +54,39 @@ export default function Login() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge guest wishlist to account
|
||||||
|
const GUEST_WISHLIST_KEY = 'woonoow_guest_wishlist';
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(GUEST_WISHLIST_KEY);
|
||||||
|
if (stored) {
|
||||||
|
const guestProductIds = JSON.parse(stored) as number[];
|
||||||
|
if (guestProductIds.length > 0) {
|
||||||
|
// Merge each product to account wishlist
|
||||||
|
const newNonce = data.nonce;
|
||||||
|
for (const productId of guestProductIds) {
|
||||||
|
try {
|
||||||
|
await fetch(`${apiRoot}/account/wishlist`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'X-WP-Nonce': newNonce,
|
||||||
|
},
|
||||||
|
credentials: 'include',
|
||||||
|
body: JSON.stringify({ product_id: productId }),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
// Skip if product already in wishlist or other error
|
||||||
|
console.debug('Wishlist merge skipped for product:', productId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Clear guest wishlist after merge
|
||||||
|
localStorage.removeItem(GUEST_WISHLIST_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to merge guest wishlist:', e);
|
||||||
|
}
|
||||||
|
|
||||||
toast.success('Login successful!');
|
toast.success('Login successful!');
|
||||||
|
|
||||||
// Set the target URL with hash route, then force reload
|
// Set the target URL with hash route, then force reload
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
|
|||||||
import { useParams, Link, useSearchParams } from 'react-router-dom';
|
import { useParams, Link, useSearchParams } from 'react-router-dom';
|
||||||
import { useThankYouSettings } from '@/hooks/useAppearanceSettings';
|
import { useThankYouSettings } from '@/hooks/useAppearanceSettings';
|
||||||
import Container from '@/components/Layout/Container';
|
import Container from '@/components/Layout/Container';
|
||||||
import { CheckCircle, ShoppingBag, Package, Truck } from 'lucide-react';
|
import { CheckCircle, ShoppingBag, Package, Truck, User } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { formatPrice } from '@/lib/currency';
|
import { formatPrice } from '@/lib/currency';
|
||||||
import { apiClient } from '@/lib/api/client';
|
import { apiClient } from '@/lib/api/client';
|
||||||
@@ -16,6 +16,7 @@ export default function ThankYou() {
|
|||||||
const [relatedProducts, setRelatedProducts] = useState<any[]>([]);
|
const [relatedProducts, setRelatedProducts] = useState<any[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const isLoggedIn = (window as any).woonoowCustomer?.user?.isLoggedIn;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchOrderData = async () => {
|
const fetchOrderData = async () => {
|
||||||
@@ -186,6 +187,7 @@ export default function ThankYou() {
|
|||||||
: 'Thank you for your business!'}
|
: 'Thank you for your business!'}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row gap-3 justify-center">
|
||||||
{elements.continue_shopping_button && (
|
{elements.continue_shopping_button && (
|
||||||
<Link to="/shop">
|
<Link to="/shop">
|
||||||
<Button size="lg" className="gap-2">
|
<Button size="lg" className="gap-2">
|
||||||
@@ -194,6 +196,15 @@ export default function ThankYou() {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
|
{isLoggedIn && (
|
||||||
|
<Link to="/my-account">
|
||||||
|
<Button size="lg" variant="outline" className="gap-2">
|
||||||
|
<User className="w-5 h-5" />
|
||||||
|
Go to Account
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -430,17 +441,25 @@ export default function ThankYou() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Continue Shopping Button */}
|
{/* Action Buttons */}
|
||||||
|
<div className="text-center flex flex-col sm:flex-row gap-3 justify-center">
|
||||||
{elements.continue_shopping_button && (
|
{elements.continue_shopping_button && (
|
||||||
<div className="text-center">
|
|
||||||
<Link to="/shop">
|
<Link to="/shop">
|
||||||
<Button size="lg" className="gap-2">
|
<Button size="lg" className="gap-2">
|
||||||
<ShoppingBag className="w-5 h-5" />
|
<ShoppingBag className="w-5 h-5" />
|
||||||
Continue Shopping
|
Continue Shopping
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
|
{isLoggedIn && (
|
||||||
|
<Link to="/my-account">
|
||||||
|
<Button size="lg" variant="outline" className="gap-2">
|
||||||
|
<User className="w-5 h-5" />
|
||||||
|
Go to Account
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Related Products */}
|
{/* Related Products */}
|
||||||
{elements.related_products && relatedProducts.length > 0 && (
|
{elements.related_products && relatedProducts.length > 0 && (
|
||||||
|
|||||||
Reference in New Issue
Block a user