feat: Add-to-cart from URL parameters

Implements direct-to-cart functionality for landing page CTAs.

Features:
- Parse URL parameters: ?add-to-cart=123
- Support simple products: ?add-to-cart=123
- Support variable products: ?add-to-cart=123&variation_id=456
- Support quantity: ?add-to-cart=123&quantity=2
- Auto-navigate to cart after adding
- Clean URL after adding (remove parameters)
- Toast notification on success/error

Usage examples:
1. Simple product:
   https://site.com/store?add-to-cart=332

2. Variable product:
   https://site.com/store?add-to-cart=332&variation_id=456

3. With quantity:
   https://site.com/store?add-to-cart=332&quantity=3

Flow:
- User clicks CTA on landing page
- Redirects to SPA with add-to-cart parameter
- SPA loads, hook detects parameter
- Adds product to cart via API
- Navigates to cart page
- Shows success toast

Works with both SPA modes:
- Full SPA: loads shop, adds to cart, navigates to cart
- Checkout Only: loads cart, adds to cart, stays on cart
This commit is contained in:
Dwindi Ramadhana
2025-12-30 20:54:54 +07:00
parent 2c4050451c
commit 93523a74ac
4 changed files with 116 additions and 2 deletions

View File

@@ -0,0 +1,95 @@
import { useEffect } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { toast } from 'sonner';
/**
* Hook to handle add-to-cart from URL parameters
* Supports both simple and variable products
*
* URL formats:
* - Simple product: ?add-to-cart=123
* - Variable product: ?add-to-cart=123&variation_id=456
* - With quantity: ?add-to-cart=123&quantity=2
*/
export function useAddToCartFromUrl() {
const navigate = useNavigate();
const location = useLocation();
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const productId = params.get('add-to-cart');
if (!productId) return;
const variationId = params.get('variation_id');
const quantity = parseInt(params.get('quantity') || '1', 10);
console.log('[WooNooW] Add to cart from URL:', {
productId,
variationId,
quantity,
fullUrl: window.location.href,
});
// Add product to cart
addToCart(productId, variationId, quantity)
.then(() => {
// Remove URL parameters after adding to cart
const cleanUrl = window.location.pathname + window.location.hash;
window.history.replaceState({}, '', cleanUrl);
// Navigate to cart if not already there
if (!location.pathname.includes('/cart')) {
navigate('/cart');
}
})
.catch((error) => {
console.error('[WooNooW] Failed to add product to cart:', error);
toast.error('Failed to add product to cart');
});
}, []); // Run once on mount
}
async function addToCart(
productId: string,
variationId: string | null,
quantity: number
): Promise<void> {
const apiRoot = (window as any).woonoowCustomer?.apiRoot || '/wp-json/woonoow/v1';
const nonce = (window as any).woonoowCustomer?.nonce || '';
const body: any = {
product_id: parseInt(productId, 10),
quantity,
};
if (variationId) {
body.variation_id = parseInt(variationId, 10);
}
console.log('[WooNooW] Adding to cart:', body);
const response = await fetch(`${apiRoot}/cart/add`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-WP-Nonce': nonce,
},
credentials: 'include',
body: JSON.stringify(body),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || 'Failed to add to cart');
}
const data = await response.json();
if (!data.success) {
throw new Error(data.message || 'Failed to add to cart');
}
console.log('[WooNooW] Product added to cart:', data);
toast.success('Product added to cart');
}