/* |-------------------------------------------------------------------------- | DR WISHLIST — SINGLE SOURCE OF TRUTH FOR "ADD/REMOVE WISHLIST" |-------------------------------------------------------------------------- | | Every wishlist trigger on the site (shop grid, home grid, product page, | wishlist page) funnels through DRWishlist so the AJAX call, badge | refresh and Meta Pixel "AddToWishlist" event only ever exist in one | place instead of being copy-pasted into every Blade view. | */ window.DRWishlist = window.DRWishlist || {}; /* |-------------------------------------------------------------------------- | UPDATE WISHLIST BADGES |-------------------------------------------------------------------------- */ DRWishlist.updateBadge = function (count) { count = parseInt(count || 0, 10); $('#globalWishListCount, .wishlist-count-badge').each(function () { let badge = $(this); badge.text(count); if (count > 0) { badge.removeClass('d-none'); } else { badge.addClass('d-none'); } }); $('.wishlist-count').text(count + (count === 1 ? ' item' : ' items')); }; /* |-------------------------------------------------------------------------- | TRACK ADD TO WISHLIST (META PIXEL) |-------------------------------------------------------------------------- */ DRWishlist.trackAddToWishlist = function (payload) { if (typeof fbq !== 'function') { return; } // Meta's catalog is keyed by SKU, not our internal product_id — the // content_ids sent here must match the catalog's "Content ID" field // or events show up as "not matching any catalog" in Events Manager. let catalogId = String(payload.sku || payload.product_id); fbq('track', 'AddToWishlist', { content_ids: [catalogId], content_type: 'product', content_name: payload.name || '', value: parseFloat(payload.price) || 0, currency: 'GBP' }); }; /* |-------------------------------------------------------------------------- | ADD TO WISHLIST |-------------------------------------------------------------------------- */ DRWishlist.add = function (payload, options) { options = options || {}; $.ajax({ url: '/ajax/wishlist/add', type: 'POST', data: payload, beforeSend: function () { if (options.button) { options.button.addClass('loading'); } }, success: function (response) { if (options.button) { options.button.removeClass('loading').addClass('active'); } DRWishlist.updateBadge(response.count); DRWishlist.trackAddToWishlist(payload); if (typeof options.onSuccess === 'function') { options.onSuccess(response); } }, error: function (xhr) { if (options.button) { options.button.removeClass('loading'); } console.error('Add to wishlist failed:', xhr); if (typeof options.onError === 'function') { options.onError(xhr); } } }); }; /* |-------------------------------------------------------------------------- | REMOVE FROM WISHLIST |-------------------------------------------------------------------------- */ DRWishlist.remove = function (productId, options) { options = options || {}; $.ajax({ url: '/ajax/wishlist/remove', type: 'POST', data: { product_id: productId }, success: function (response) { DRWishlist.updateBadge(response.count); if (typeof options.onSuccess === 'function') { options.onSuccess(response); } }, error: function (xhr) { console.error('Remove from wishlist failed:', xhr); if (typeof options.onError === 'function') { options.onError(xhr); } } }); }; /* |-------------------------------------------------------------------------- | "ADD" TRIGGER — shop grid / home grid / product listing cards |-------------------------------------------------------------------------- | | These buttons only ever add (they don't toggle off once active), which | matches the product listing UX across the site. | */ $(document).on( 'click', '.addToWishlist', function () { let button = $(this); if (button.hasClass('active') || button.hasClass('loading')) { return; } DRWishlist.add({ product_id: button.attr('data-product-id') || button.data('product-id'), sku: button.attr('data-sku') || button.data('sku'), name: button.attr('data-name') || button.data('name'), slug: button.attr('data-slug') || button.data('slug'), image: button.attr('data-image') || button.data('image'), price: button.attr('data-price') || button.data('price'), colors: button.attr('data-colors') }, { button: button }); } ); /* |-------------------------------------------------------------------------- | TOGGLE TRIGGER — single-product wishlist button (PDP) |-------------------------------------------------------------------------- */ $(document).on( 'click', '.wishlist-toggle-btn', function () { let button = $(this); if (button.hasClass('loading')) { return; } if (button.hasClass('active')) { button.addClass('loading'); DRWishlist.remove(button.data('product-id'), { onSuccess: function () { button.removeClass('active loading').html('♡'); }, onError: function () { button.removeClass('loading'); } }); return; } DRWishlist.add({ product_id: button.data('product-id'), sku: button.data('sku'), name: button.data('name'), slug: button.data('slug'), image: button.data('image'), price: button.data('price'), colors: button.attr('data-colors') }, { button: button, onSuccess: function () { button.html('♥'); } }); } ); /* |-------------------------------------------------------------------------- | REMOVE TRIGGER — wishlist page card |-------------------------------------------------------------------------- */ $(document).on( 'click', '.removeWishlistItem', function () { let button = $(this); let productId = button.data('id'); let card = button.closest('.col-xl-4, .col-md-6'); button.prop('disabled', true); DRWishlist.remove(productId, { onSuccess: function (response) { card.remove(); if (response.count <= 0) { $('.row.g-4').html(`

Your wishlist is empty

Save your favorite products here.

`); } }, onError: function () { button.prop('disabled', false); } }); } );