Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterbrunton/ad8b96294bcbc5cd42a24f97093bae81 to your computer and use it in GitHub Desktop.
Save peterbrunton/ad8b96294bcbc5cd42a24f97093bae81 to your computer and use it in GitHub Desktop.
Product injection based on cart conditions. GWP offers
This gist creates a gift with purchase function that injects a product or multiple products into the cart when conditions are met.
**It is important to mention this function does not perform any discount that works separately current implementation is performed by the Shopify Scripts App
*This function is not considered good UX practice.
The conditions are cart threshold amount met or/and cart threshold amount met and conditional product applied to the cart.
When the conditions are no longer met the products are removed from the cart.
Create a new section, sections/gwp-injection.liquid
```
{% if section.settings.enable %}
<gwp-product-injection class="hidden">
{% for block in section.blocks %}
<li
class="gwp-product-injection--item"
data-variant-id="{{ all_products[block.settings.product].first_available_variant.id }}"
data-product-title="{{ all_products[block.settings.product].title }}"
data-injection-threshold="{{ block.settings.threshold | append: '00' | times: 1 }}"
data-product-image="{{ block.settings.threshold | append: '00' | times: 1 }}"
data-discount-title="{{ block.settings.discount_title }}"
{% if block.settings.conditional_product %}
data-conditional-product="{{ all_products[block.settings.conditional_product].handle }}"
{% endif %}
>
<span>Your free gift, {{ all_products[block.settings.product].title }} has been added to the cart.</span>
</li>
{% endfor %}
</gwp-product-injection>
<script defer>
if (!customElements.get('gwp-product-injection')) {
customElements.define('gwp-product-injection', class GWPProductInjection extends HTMLElement {
constructor() {
super();
this.GWPs = new Map(); // Store GWPs as a Map
this.boundHandleGWP = this.handleGWP.bind(this);
document.addEventListener('cart:change', this.boundHandleGWP);
document.addEventListener('cart:refresh', this.boundHandleGWP);
}
connectedCallback() {
this.populateGWPs();
this.cart = document.querySelector('cart-notification') || document.querySelector('cart-drawer');
this.cartItemsHandles = new Set();
}
populateGWPs() {
// Populate GWPs from the list
[...document.querySelectorAll('.gwp-product-injection--item')].forEach((GWP) => {
const variantId = GWP.dataset.variantId;
this.GWPs.set(variantId, {
threshold: Number(GWP.dataset.injectionThreshold),
conditionalProduct: GWP.dataset.conditionalProduct,
added: false,
variantId: variantId
});
});
}
setStatusGWPProductInjection(varId, status) {
const gwp = this.GWPs.get(varId);
if (gwp) {
gwp.added = status;
}
}
getStatusGWPProductInjection(varId) {
const gwp = this.GWPs.get(varId);
return gwp ? gwp.added : false;
}
disconnectedCallback() {
document.removeEventListener('cart:change', this.boundHandleGWP);
}
async getCurrentCartData() {
let fetchAdmin = {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
};
let data = await fetch('/cart.js', fetchAdmin).then((response) => response.json());
return data;
}
async fireGWP(var_id) {
this.setStatusGWPProductInjection(var_id, true);
const body = JSON.stringify({
items: [
{
id: var_id,
quantity: 1,
},
],
});
await fetch(`${Shopify.routes.root}cart/add.js`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: body,
})
.then((response) => response.json())
.then((parsedState) => {
document.documentElement.dispatchEvent(new CustomEvent('theme:loading:start', { bubbles: true }));
document.documentElement.dispatchEvent(new CustomEvent('cart:refresh', { bubbles: true }));
document.documentElement.dispatchEvent(new CustomEvent('theme:loading:end', { bubbles: true }));
})
.catch((e) => {
console.error(e);
this.dispatchEvent(new CustomEvent('cart:error', { bubbles: true }));
})
.finally(() => {
this.handleGWP(); // Check for other eligible GWPs after adding one
});
}
async removeGWP(var_id) {
this.setStatusGWPProductInjection(var_id, false);
const body = JSON.stringify({
quantity: 0,
id: var_id,
});
await fetch('/cart/change', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
body: body,
})
.then((response) => response.json())
.then((parsedState) => {
document.documentElement.dispatchEvent(new CustomEvent('theme:loading:start', { bubbles: true }));
document.documentElement.dispatchEvent(new CustomEvent('cart:refresh', { bubbles: true }));
document.documentElement.dispatchEvent(new CustomEvent('theme:loading:end', { bubbles: true }));
})
.catch((e) => {
console.error(e);
})
}
isConditionalProductInCart(conditionalProduct, cartData) {
if (!conditionalProduct) return true;
const cartItems = cartData.items || [];
return cartItems.some((item) => item.handle === conditionalProduct);
}
async handleGWP() {
console.log('Handling GWP...');
const cartData = await this.getCurrentCartData();
console.log('Cart Data:', cartData);
// Create a copy of the cart items handles for easy lookup
this.cartItemsHandles = new Set(cartData.items.map(item => item.handle));
// Check if added GWPs should be removed
for (const gwp of Array.from(this.GWPs.values()).filter(gwp => gwp.added)) {
if ((gwp.conditionalProduct && !this.cartItemsHandles.has(gwp.conditionalProduct)) || gwp.threshold > cartData.total_price) {
// Conditional product is not in the cart anymore, or cart total is below the GWP threshold
await this.removeGWP(gwp.variantId);
}
}
// Add all eligible GWPs to the cart
for (const [variantId, gwp] of this.GWPs) {
if (gwp.added) continue; // Skip the ones already added
if ((gwp.conditionalProduct && this.cartItemsHandles.has(gwp.conditionalProduct)) || (!gwp.conditionalProduct && gwp.threshold <= cartData.total_price)) {
await this.fireGWP(variantId);
}
}
}
});
}
</script>
{% endif %}
{% schema %}
{
"name": "t:sections.gwp_injection.name",
"tag": "section",
"class": "section",
"disabled_on": {
"groups": ["header", "custom.overlay", "footer"]
},
"settings": [
{
"type": "checkbox",
"id": "enable",
"label": "t:sections.gwp_injection.enable"
}
],
"blocks": [
{
"type": "gwp_item",
"name": "t:sections.gwp_injection.blocks.gwp_item.name",
"limit": 3,
"settings": [
{
"type": "product",
"id": "product",
"label": "t:sections.gwp_injection.blocks.gwp_item.product"
},
{
"type": "text",
"id": "threshold",
"label": "t:sections.gwp_injection.blocks.gwp_item.threshold"
},
{
"type": "text",
"id": "discount_title",
"label": "t:sections.gwp_injection.blocks.gwp_item.discount"
},
{
"type": "product",
"id": "conditional_product",
"label": "Conditional product",
"info": "The gwp is only fired when this product is in the cart"
}
]
}
],
"presets": [
{
"name": "t:sections.gwp_injection.name",
"blocks": [
{
"type": "gwp_item"
}
]
}
]
}
{% endschema %}
```
In layout/theme.liquid
Add the section to the theme file just inside the body tag
```{%- section 'gwp-injection' -%}```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment