Direkt zum Inhalt
/**
* FREE GIFT AUTOMATION — Waytoplay Toys
* Automatically adds the Flyer (free gift) when cart reaches €75,
* and removes it when the cart drops below €75.
*
* Works alongside the existing "Happy Holidays!" Buy X Get Y discount
* which handles the price reduction at checkout.
*
* Theme: Showcase
* Free gift: Flyer (variant ID 48114081399130)
* Threshold: €75.00
*/
(function () {
const FREE_GIFT_VARIANT_ID = 48114081399130;
const THRESHOLD_CENTS = 7500; // €75.00
const GIFT_PROPERTY_KEY = '_free_gift';
const GIFT_PROPERTY_VALUE = 'happy-holidays';
let isEvaluating = false;
// ── Helpers ────────────────────────────────────────────────────────────────
async function fetchCart() {
const res = await fetch('/cart.js');
return res.json();
}
async function addGift() {
await fetch('/cart/add.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: FREE_GIFT_VARIANT_ID,
quantity: 1,
properties: { [GIFT_PROPERTY_KEY]: GIFT_PROPERTY_VALUE },
}),
});
}
async function removeGift() {
// Find the specific line number of the gift (1-based index in Shopify)
const cart = await fetchCart();
const lineIdx = cart.items.findIndex(isGiftItem);
if (lineIdx === -1) return;
await fetch('/cart/change.js', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ line: lineIdx + 1, quantity: 0 }),
});
}
function isGiftItem(item) {
return (
item.variant_id === FREE_GIFT_VARIANT_ID &&
item.properties &&
item.properties[GIFT_PROPERTY_KEY] === GIFT_PROPERTY_VALUE
);
}
/**
* Subtotal excluding the gift itself, so its price doesn't affect
* the threshold calculation.
*/
function subtotalWithoutGift(cart) {
return cart.items.reduce((sum, item) => {
return isGiftItem(item) ? sum : sum + item.line_price;
}, 0);
}
// ── Refresh the Showcase cart drawer ──────────────────────────────────────
function refreshCart() {
// Showcase theme uses a custom element / event-based cart drawer.
// Try the most common event names used by Showcase and similar themes:
document.dispatchEvent(new CustomEvent('cart:refresh'));
document.dispatchEvent(new CustomEvent('theme:cart:update'));
// Also dispatch on window for themes that listen there:
window.dispatchEvent(new CustomEvent('cart:refresh'));
// Re-fetch and update the cart count badge if the theme exposes it:
fetch('/cart.js')
.then(r => r.json())
.then(cart => {
// Update any cart count elements the theme renders
document.querySelectorAll('[data-cart-count], .cart-count, .js-cart-count').forEach(el => {
el.textContent = cart.item_count;
});
})
.catch(() => {});
}
// ── Core logic ─────────────────────────────────────────────────────────────
async function evaluateGift() {
if (isEvaluating) return;
isEvaluating = true;
try {
const cart = await fetchCart();
const subtotal = subtotalWithoutGift(cart);
const giftInCart = cart.items.some(isGiftItem);
const qualifies = subtotal >= THRESHOLD_CENTS;
if (qualifies && !giftInCart) {
await addGift();
refreshCart();
} else if (!qualifies && giftInCart) {
await removeGift();
refreshCart();
}
} catch (e) {
console.warn('[FreeGift]', e);
} finally {
isEvaluating = false;
}
}
// ── Intercept cart mutations ───────────────────────────────────────────────
const _origFetch = window.fetch;
window.fetch = async function (...args) {
const url = typeof args[0] === 'string' ? args[0] : (args[0]?.url ?? '');
const isCartMutation = /\/cart\/(add|change|update)\.js/.test(url);
const response = await _origFetch.apply(this, args);
if (isCartMutation) {
// Wait a tick so the cart has settled before we evaluate
response.clone().json().then(() => {
setTimeout(evaluateGift, 300);
}).catch(() => {});
}
return response;
};
// ── Check on page load ────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', evaluateGift);
})();