AppmakerDeveloper

How to add free gift products to cart

This guide will help you to add free gift products to cart.

Introduction

This guide will help you to add free gift products to cart.

Hooks

manage-cart-after-response

This hook helps to manage the cart after the response.It can be used to add free gift products to cart.

 appmakerFunctions.registerAppmakerFn({
      trigger: 'manage-cart-after-response', 
      namespace: 'free-gift', // your namespace
      function: AddFreeGiftToCart, // your function
    });

Steps

  1. Use the manage-cart-after-response hook to add free gift products to cart. You can register the hook in the index.js file.
import appmakerFunctions from '@appmaker-xyz/core';
import addFreeGift from './lib/addFreeGift'; // your function file

export function activate() {
  appmakerFunctions.registerAppmakerFn({
    trigger: 'manage-cart-after-response', 
    namespace: 'free-gift', // your namespace
    function: addFreeGift, // your function
  });
}

// Register the plugin or theme. let's take example of plugin
const Plugin = {
  id: 'custom-free-gift', // your plugin id
  name: 'custom-free-gift', // your plugin name
  activate,
}; 

export default Plugin;
  1. Create a function file addFreeGift.js in a folder lib and add the following code.
import { parse } from '@appmaker-xyz/core';
import { shopifyIdHelper, productsStore } from '@appmaker-xyz/shopify';
import getConditionalCartSum from './lib/getConditionalCartSum'; // your function to get cart total
import { isEmpty } from 'lodash';

export function addFreeGift(input, dependencies) {
  
    const {
      cartHelpers: { hasVariantInCart },
      coreDispatch,
    } = dependencies;

    const freeGiftsToAdd = [];
    const existingFreeGifts = [];
    const freeGiftToRemove = [];

    let nextFreeGiftIndex = false;
    let cartTotals = [];

    // Logger for debugging
    function log(...args) {
      if (debugLog) {
        console.log('free-gift', ...args);
      }
    }

    const freeGiftDependencies = {
      log,
      ...dependencies,
    };

    // set cart message if any
    function setCartMessage(message, product) {
      coreDispatch({
        type: 'SET_APP_STORAGE_STATE',
        value: {
          cart_custom_message: message,
          next_free_gift_product: product,
        },
      });
    }     


    // get freeGifts
    const freeGifts = getFreeGifts(); // your function to get free gifts

    if (Array.isArray(freeGifts) && freeGifts.length > 0) {
      // Sort freeGift by cart_value_amount
      freeGifts.sort((a, b) => a.cart_value_amount - b.cart_value_amount);
      nextFreeGiftIndex = 0;

      for (const [freeGiftIndex, gift] of freeGifts.entries()) {        

        const cartTotal = getConditionalCartSum({
          input,
          cartSumCondition: gift?.cart_value_condition,
          collections: gift?.collections,
          dependencies: freeGiftDependencies,
        }); // your function to get cart total

        cartTotals[freeGiftIndex] = cartTotal;

        const { products, cart_value_amount } = gift;
        const cart_value_required = parseFloat(cart_value_amount);
        if (cartTotal >= cart_value_required) {
          log('free gift condition met', {
            cartTotal,
            cart_value_required,
          });

          for (const product of products) {
            

            let availableVariantId = await getAvailableVariant();// your function to get available variant

            // Check if product is available
            if (
              availableVariantId &&
              !hasVariantInCart(input, availableVariantId)
            ) {
              log('Adding free gift', availableVariantId);

              freeGiftsToAdd.push({
                variantId: availableVariantId,
                quantity: 1,
                customAttributes: [
                  {
                    key: 'appmaker_free_gift',
                    value: 'Free Gift',
                  },
                  ...(gift?.attributes || []),
                ],
              });
            } else if (availableVariantId) {
              log('Free gift already in cart', availableVariantId);
              existingFreeGifts.push(availableVariantId);
            } else {
              log('Not adding as all gift unavailable', availableVariantId);
            }
          }
          nextFreeGiftIndex = freeGifts[freeGiftIndex + 1]
            ? freeGiftIndex + 1
            : false;
        } else if (cartTotal < cart_value_required) {
          log('free gift condition not met', {
            cartTotal,
            cart_value_required,
          });
        }
      }

      // Remove all non-elgible free gifts
      if (input?.currentCart?.lineItems?.edges?.length > 0) {
        input.currentCart.lineItems.edges.forEach((item) => {
          const currentLineItem = item.node;
          const { customAttributes } = currentLineItem;
          const isFreeGift = customAttributes.find(
            (attribute) => attribute.key === 'appmaker_free_gift',
          );
          if (isFreeGift) {
            const variantId = currentLineItem.variant.id;
            // check if variant is in existingFreeGifts
            const isExistingFreeGift = existingFreeGifts.find(
              (existingFreeGiftItem) => existingFreeGiftItem === variantId,
            );
            if (!isExistingFreeGift) {
              log('Removing free gift', variantId);
              freeGiftToRemove.push({
                id: currentLineItem.id,
                variantId: variantId,
                quantity: 0,
              });
            }
          } else {
            // log('not a free gift', currentLineItem);
          }
        });
      }

      log('nextFreeGiftIndex', nextFreeGiftIndex);

      /**
       * Add free gift message if any free gift is pending to add and message is set
       */
      if (
        nextFreeGiftIndex !== false &&
        !isEmpty(freeGifts[nextFreeGiftIndex].pre_offer_apply_cart_message)
      ) {
        const nextFreeGift = freeGifts[nextFreeGiftIndex];
        const difference =
          nextFreeGift.cart_value_amount - cartTotals[nextFreeGiftIndex];

        const cartMessage = parse({
          template: {
            message: nextFreeGift.pre_offer_apply_cart_message,
          },
          data: {
            difference: difference.toFixed(2),
            currentCart: input.currentCart,
            freeGift: nextFreeGift,
          },
        });

        log('cart message added', cartMessage?.message);
        const nextFreeGiftProduct = nextFreeGift?.products[0]?.product_gid;
        const productData = await productsStore.getProduct(
          shopifyIdHelper(nextFreeGiftProduct),
        );
        log('productData', nextFreeGift.products[0].product_gid, productData);
        setCartMessage(cartMessage?.message, { productData, nextFreeGift });
      } else {
        log('cart message removed');
        setCartMessage('', {});
      }
    }

    // Merge freeGiftsToAdd and lineItemsToUpdate to input. This will be used to update cart

    input.lineItemsToAdd = input.lineItemsToAdd
      ? [...input.lineItemsToAdd, ...freeGiftsToAdd]
      : freeGiftsToAdd;

    input.lineItemsToUpdate = input.lineItemsToUpdate
      ? [...input.lineItemsToUpdate, ...freeGiftToRemove]
      : freeGiftToRemove;

    return input;
};

Above is a sample code for free gift. You can use this as a reference to create your own custom logic.