Setting Up the Extend Cart Offers

Overview

This article covers inserting the logic and layout code for the Extend cart offers component

Est. Time of Completion: 1:30 hour(s)

Create extend.js

  1. Go to /assets/js/theme/ and create a new file named extend.js. Paste the snippet below inside of it:
// initializeCartOffer()
// Initializes cart offer div tag elements with createAjaxOfferElements() then renders the buttons using Extend sdk
export function initializeCartOffers() {
    // Ensuring Extend and ExtendBigCommerce are on the window before running
    if (window.Extend && window.ExtendBigCommerce) {
        // Slice - utility use
        var slice = Array.prototype.slice;

        /**********************/
        /*   util functions   */
        /**********************/

        // FindAll function handles browser compatability issues for querySelectorAll
        function findAll(element) {
            var items = document.querySelectorAll(element);
            return items ? slice.call(items, 0) : [];
        }

        // addPlanToCart injects offers into cart using ExtendBigCommerce SDK, taking in params
        function addPlanToCart(sku, plan, quantity, cart) {
            ExtendBigCommerce.addPlanToCart(
                {
                    sku: sku,
                    plan: plan,
                    quantity: quantity,
                    cart: cart,
                },
                function (err) {
                    if (err) {
                        return;
                    } else {
                        return window.location.reload();
                    }
                }
            );
        }

        // Get's updated cart object
        ExtendBigCommerce.getCart(function (error, cart) {
            if (cart) {
                // Run normalization
                ExtendBigCommerce.normalizeCart(
                    { cart: cart, balance: true },
                    function (err, data) {
                        if (data && data.updates) {
                            if (Extend.analytics) {
                                return window.dispatchEvent(
                                    new CustomEvent("normalization", {
                                        detail: { updates: data.updates },
                                    })
                                );
                            } else {
                                window.location.reload();
                            }
                        }
                    }
                );

                // Initial empty state
                var planToName = {};

                // Goes through customItems grabbing the warranties and maps the productName to the warranty id in planToName
                cart.lineItems.customItems.forEach(function (cItem) {
                    if (cItem && cItem.sku && cItem.sku.indexOf(";xtd;") > -1) {
                        var customId = cItem.id;
                        var sku = cItem.sku.split(";xtd;")[1];
                        var product = cart.lineItems.physicalItems.find(function (pItem) {
                            if (pItem.sku === sku) {
                                return pItem.name;
                            }
                        });
                        if (product && product.name) {
                            planToName[customId] = product.name;
                        }
                    }
                });

                // Find all plan items, and display product information
                findAll("#extend-plan-item").forEach(function (el) {
                    var cartItemId = el.getAttribute("data-extend-itemid");
                    if (planToName[cartItemId]) {
                        el.innerText = "For: " + planToName[cartItemId];
                    }
                });

                // Find all cart offers and render the instance
                findAll("#extend-cart-offer").forEach(function (el) {
                    var sku = el.getAttribute("data-extend-sku");
                    var quantity = el.getAttribute("data-extend-quantity");
                    var itemId = el.getAttribute("data-extend-item-id");

                    if (!sku) {
                        return;
                    }

                    // If there's already an instance or warranty is already in cart do not render offer
                    if (
                        Extend.buttons.instance(el) ||
                        ExtendBigCommerce.warrantyAlreadyInCart(sku, cart)
                    ) {
                        return;
                    }

                    // Cart offer render
                    Extend.buttons.renderSimpleOffer(el, {
                        referenceId: sku,
                        onAddToCart: function (offer) {
                            // If there's only one item call addPlanToCart immediately
                            if (cart.lineItems.physicalItems.length === 1) {
                                addPlanToCart(sku, offer.plan, quantity, cart);
                                return;
                            }

                            // Show loading overlay
                            document
                                .querySelector("[data-cart] .loadingOverlay")
                                .setAttribute("style", "display: block");

                            // Find cart item, and get id and options from cartItem
                            var cartItem = cart.lineItems.physicalItems.find(
                                (it) => it.id == itemId
                            );
                            var cartId = cart.id;
                            var options = cartItem.options.reduce((optArray, opt) => {
                                var optId = opt.nameId;
                                var optValue = opt.valueId === null ? opt.value : opt.valueId;
                                return (optArray = [
                                    ...optArray,
                                    { optionId: optId, optionValue: optValue },
                                ]);
                            }, []);

                            // For multiple items we have to remove the cart items, and add them back with respective plans so it shows the plan underneath the respective product
                            ExtendBigCommerce.deleteCartItem({ cartId, itemId }, function () {
                                ExtendBigCommerce.addCartItem(
                                    {
                                        cartId: cartId,
                                        productId: cartItem.productId,
                                        variantId: cartItem.variantId,
                                        quantity: quantity,
                                        optionSelections: options || [],
                                    },
                                    function () {
                                        addPlanToCart(sku, offer.plan, quantity, cart);
                                    }
                                );
                            });
                        },
                    });
                });
            }
        });
    }
}

// initializeAftermarket()
// Initializes post purchase lead modals when the app loads if we find a leadToken in the URL
export function initializeAftermarket() {
    if (window.Extend && window.ExtendBigCommerce) {
        function getQueryParam(name) {
            var results = new RegExp("[?&]" + name + "=([^&#]*)").exec(
                window.location.href
            );
            if (results == null) {
                return null;
            }
            return decodeURI(results[1]) || 0;
        }

        // Searches URL for both leadToken or leadtoken and sets it to the leadToken variable
        let leadToken = getQueryParam("leadToken")
            ? (leadToken = getQueryParam("leadToken"))
            : (leadToken = getQueryParam("leadtoken"));

        // If leadToken show modal containing lead offer
        if (leadToken) {
            window.Extend.aftermarketModal.open({
                leadToken,
                onClose: function (plan, product, quantity) {
                    if (plan && product) {
                        ExtendBigCommerce.addPlanToCart(
                            {
                                sku: product.id,
                                plan,
                                quantity,
                                leadToken,
                            },
                            function (err, data) {
                                if (err) {
                                    console.error(err);
                                    return;
                                } else if (!data || !data.cartUrl) {
                                    window.location = "/cart.php";
                                } else {
                                    window.location = data.cartUrl;
                                }
                            }
                        );
                    }
                },
            });
        }
    }
}


Import & Invoke initializeCartOffers Function

  1. Open the file /assets/js/theme/cart.js and copy the snipet below and paste it at the top of the file, right under all the other import statements:
// Extend - Import initializeCartOffers()
import { initializeCartOffers } from './extend.js';
// Extend - End Code

  1. In the same file, find the onReady() function inside the Cart class add the snippet below to invoke the function:
// Extend - Initialize cart offers when cart is initialy loaded
initializeCartOffers();
// Extend - End Code


Handle Cart Change

When the cart changes, we will need to invoke our initializeCartOffers() to ensure Extend renders correctly within the cart again.

  1. In the same file, scroll down and find the utils.api.cart.getContent() function. Copy the snippet below to invoke the function every time the cart updates:
// Extend - Initialize Cart Offers after the cart update 
initializeCartOffers();
// Extend - End Extend Code 


Style warranties in cart

Now that we can add warranties to the cart, we need to ensure they're styling correctly following the instructions below.

  1. Open the file /templates/components/cart/content.html
  2. Copy the snippet below:
    <!-- Extend - Add Extend Logo for Warranties -->
    {{else if type '==' 'Custom'}}
    <img
         class="cart-item-fixed-image"
         data-sizes="auto"
         src="{{cdn 'img/extend_logo.png'}}"
         data-src="{{cdn ../theme_settings.default_image_extend}}"
         alt="Extend Protection Plan"
         title="Extend Protection Plan"
         />
    <!-- Extend - End Extend Code -->
    
  3. Find the code {{#if type '==' 'GiftCertificate'}} and paste the snippet after the <img> element, but before the closing {{else}} statement:

  1. In the same file, find the block that starts with {{#if brand.name}}. Locate the <h2> element containing the <a> tag and delete the whole of the <h2> and all it's contents.
  2. Then copy and paste in the snippet below, which will replace the <h2> and <a> we previously removed:
<!-- Extend - Remove links for warranties -->
{{#if type '==' 'Custom'}}
<h4 class="cart-item-name"><p>{{name}}</p></h4>
{{else}}
<h2 class="cart-item-name">
  <a class="cart-item-name__label" href="{{url}}">{{name}}</a>
</h2>
{{/if}}
<!-- Extend - End Extend Code -->

The resulting code changes should look similar to the screenshot below:

  1. In the same file, find the block that starts with {{#if options}} and paste the snippet below:
<!-- Extend - Render item info -->
{{#if type '==' 'Custom'}}
<div id="extend-plan-item" data-extend-itemid={{id}}></div>
{{/if}}
<!-- Extend - End Extend Code -->

<!-- Extend - Adds Cart Offer -->
<div
     id="extend-cart-offer"
     data-extend-item-id="{{id}}"
     data-extend-sku="{{sku}}"
     data-extend-quantity="{{quantity}}"
     ></div>
<!-- Extend - End Extend Code -->

Similarly, these code changes should result


Verify

You should now see Extend Warranties you add to the cart appear neatly formatted and contain information regarding the product they're covering along with not being clickable. The Extend warranty quantities should also always match the product they're covering quantity. If you increment the product by 1, the Extend warranty should also increment by 1 automatically.

Way to go! If you see the Extend Warranty includes an Icon and information about the product it's covering and is NOT clickable, you're ready for the next step if you have a drop down cart: Styling Drop down cart

If you do not have a drop down cart, skip to this step: Setting up Extend Analytics & After market support

If you run into any issues during this integration process or have questions please reach out to our team through your Merchant Portal.