const BXGY_ICONS = { DISCOUNT_TAG_ICON_FILL : ``, DISCOUNT_TAG_ICON_STROKE : ``, DISCOUNT_ICON_CIRCULAR : ``, } let gfgCustomDiscount = { state: { isFirstRenderForcustomDiscount: true, productFound: {}, productVariantFrount: {}, collectionFound: {}, productsDataForBuyXGetY: {}, BuyXConditionFulfilled: {}, allowedGetYQuantity: {}, customDiscountEligibilityResult: {}, }, init: function () { try { let settings = gfg.settings; let _customDiscountData = settings.customDiscount; gfgCustomDiscount.initialize(_customDiscountData); // const page_type = gfg.f.getPageType(); // if (page_type === "product") { // gfgCustomDiscount.productPage.init(_customDiscountData); // } // if (page_type === "cart") { // gfgCustomDiscount.cartPage.init(_customDiscountData); // } // if (page_type !== "cart") { // gfgCustomDiscount.sideCart.init(_customDiscountData); // } } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount init fn", error); } }, productPage: { init: function (customDiscountData) { try { gfgCustomDiscount.initialize(customDiscountData, "PRODUCT_PAGE"); } catch (error) { gfg.utility.debugConsole("Error inside productPage.init fn", error); } }, }, cartPage: { init: function (customDiscountData) { try { gfgCustomDiscount.initialize(customDiscountData, "CART_PAGE"); } catch (error) { gfg.utility.debugConsole("Error inside cartPage.init fn", error); } }, }, sideCart: { init: function (customDiscountData) { try { gfgCustomDiscount.initialize(customDiscountData, "SIDE_CART"); } catch (error) { gfg.utility.debugConsole("Error inside cartPage.init fn", error); } }, }, initialize: async function (customDiscountData, pageType) { try { const shopName = window?.Shopify?.shop; if(shopName && shopName === "heavins-ie.myshopify.com"){ let _cartDataHeavins = null; // Fetch cart data if (gfgCustomDiscount.state.isFirstRenderForcustomDiscount) { _cartDataHeavins = await gfg.utility.getFirstRenderCartData(); gfgCustomDiscount.state.isFirstRenderForcustomDiscount = false; } else { _cartDataHeavins = await gfg.utility.getCart(); } const cartAttributes = await gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getExistingCartAttributes(_cartDataHeavins); if(Object.keys(cartAttributes).length > 0){ gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.updateCartAttributesWithCFData({}); } return; } const enabledCustomDiscountData = customDiscountData?.filter((discount) => discount.isEnabled) || []; if(enabledCustomDiscountData.length == 0) { return; } let cartData = null; // Fetch cart data if (gfgCustomDiscount.state.isFirstRenderForcustomDiscount) { cartData = await gfg.utility.getFirstRenderCartData(); gfgCustomDiscount.state.isFirstRenderForcustomDiscount = false; } else { cartData = await gfg.utility.getCart(); } // Get existing data from cart attributes let previousAttrData = gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getExistingCartAttributes(cartData); let newAtttributeData = JSON.parse(JSON.stringify(previousAttrData)); // Process each discount and update the existing data for (let customDiscount of enabledCustomDiscountData) { const isActiveCampaign = gfg.customDiscountValidationFunctions.checkForActiveCampaign(customDiscount); if (!isActiveCampaign || gfg?.testAddLineItemsScriptInProgress) { continue; } // Verify custom discount rules const customDiscountEligibilityResult = await gfg.customDiscountValidationFunctions.verifyCustomDiscountRules(customDiscount, cartData); gfgCustomDiscount.state.customDiscountEligibilityResult = { [customDiscount.title]: customDiscountEligibilityResult }; const currFunctionAttributeData = await gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.init(customDiscount, cartData, customDiscountEligibilityResult); if(customDiscount?.CFDevSettings?.functionValidationLimits?.isLimitsEnabled) { // Process the discount and get the result newAtttributeData[customDiscount.title] = JSON.parse(JSON.stringify(currFunctionAttributeData)); } if(customDiscount?.discountFunctionType === "BUYX_GETY_DISCOUNT" && customDiscount?.widgetSettings) { gfgCustomDiscount.gfgBXGY.init(customDiscount?.title) } } let newDataToCompare = JSON.parse(JSON.stringify(newAtttributeData)); if(JSON.stringify(previousAttrData) != JSON.stringify(newDataToCompare)) { gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.updateCartAttributesWithCFData(newAtttributeData); } } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount initialize fn", error); } }, f: { clearExistingContent: function (elements) { try { elements.forEach((element) => { element.innerHTML = ""; }); } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount clearExistingContent fn", error); } }, appendNewUI: function (elements, uiArray, existingBlocks) { try { elements.forEach((element) => { // Append each generated UI element uiArray.forEach((uiElement) => { if (existingBlocks.length > 0) { const id = element.id; const isIdPresentInOffer = uiElement.offer._id == id; if (!isIdPresentInOffer && uiArray.length > 1) { return; } } const clone = uiElement.generatedUI.cloneNode(true); element.appendChild(clone); }); }); } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount appendNewUI fn", error); } }, insertIntoPageWrapper: function (preparedUiArray, page_type) { try { if (preparedUiArray.length === 0) { return; } const gfgCustomDiscountWrapperProductEle = gfg.utility.findWrapperElement("CUSTOM_DISCOUNT", page_type, null, 'JS'); // let selector = page_type === "PRODUCT_PAGE" ? ".gfgProductPageWrapperV2" : ".gfgCartPageWrapperV2"; // let gfgCustomDiscountWrapperProductEle = document.querySelectorAll(selector + " .gfgCustomDiscountWrapper"); //change this to custom discount wrapper later let gfgCustomDiscountBlockV2Added = document.querySelectorAll(".gfgCustomDiscountWrapperV2"); // if (gfgCustomDiscountBlockV2Added.length > 0 && page_type !== "SIDE_CART") { // gfgCustomDiscountWrapperProductEle = gfgCustomDiscountBlockV2Added; // } gfgCustomDiscount.f.clearExistingContent(gfgCustomDiscountWrapperProductEle); gfgCustomDiscount.f.appendNewUI(gfgCustomDiscountWrapperProductEle, preparedUiArray, gfgCustomDiscountBlockV2Added); } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount insertIntoPageWrapper fn", error); } }, }, utility: { createElementWithAttributes : function (tag, attributes) { try { const element = document.createElement(tag); for (let key in attributes) { if(key === "innerText" || key === "innerHTML") { element[key] = attributes[key] } else { element.setAttribute(key, attributes[key]); } } return element; } catch (error) { gfg.utility.debugConsole("error in createElementWithAttributes", error) } } }, gfgCustomDiscountFunctionLogicHandlers: { init: async function (customDiscountSettings, cartData, customDiscountEligibilityResult) { try { let newAttributeValueData; let storefrontResult; let isTieredGiftDiscount = customDiscountSettings.discountFunctionType == "TIERED_GIFTS_DISCOUNT"; let functionValidationType; let attributeValue = ""; if(cartData.items.length != 0) { const { fullValidation, allQualifyingItems, fewQualifyingItems } = customDiscountSettings?.CFDevSettings?.functionValidationLimits || {}; if(fewQualifyingItems && cartData.items.length > parseFloat(fewQualifyingItems)){ functionValidationType = "NONE"; } else if(allQualifyingItems && cartData.items.length > parseFloat(allQualifyingItems)){ functionValidationType = "FEW_QUALIFYING" } else if(fullValidation && cartData.items.length > parseFloat(fullValidation)){ functionValidationType = "ALL_QUALIFYING"; } else if (!fullValidation || cartData.items.length <= parseFloat(fullValidation)) { functionValidationType = "FULL"; } // Update or add the new data for the current title switch(customDiscountSettings.discountFunctionType) { case "CONDITIONAL_DISCOUNT" : { storefrontResult = await gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.prepareTargetsForConditionalDiscount(customDiscountSettings, cartData); break; } case "TIERED_DISCOUNT": { storefrontResult = await gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.prepareTargetsForTieredDiscount(customDiscountSettings, cartData); break; } case "TIERED_GIFTS_DISCOUNT": { storefrontResult = await gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.prepareTargetsForTieredGiftDiscount(customDiscountSettings, cartData); break; } case "BUYX_GETY_DISCOUNT": { storefrontResult = await gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.prepareTargetsForBuyXGetYDiscount(customDiscountSettings, cartData); break; } } const isValid = (input) => { if (Array.isArray(input)) { return input.length > 0; } else if (typeof input === 'object' && input !== null) { return Object.keys(input).length > 0; } return false; }; switch (functionValidationType) { case "FULL": { newAttributeValueData = ""; break; } case "ALL_QUALIFYING": { const validVariantIds = storefrontResult.validVariantIds; newAttributeValueData = (isValid(validVariantIds) && customDiscountEligibilityResult) ? { customDiscountEligibilityResult, validVariantIds } : ""; break; } case "FEW_QUALIFYING": { const validVariantIds = storefrontResult?.validVariantIds; storefrontResult.discounts = storefrontResult.discounts; //storefrontResult.discounts = this.addPrefixToProductVariantIds(storefrontResult.discounts); newAttributeValueData = (isValid(validVariantIds) && customDiscountEligibilityResult) ? { customDiscountEligibilityResult, ...storefrontResult } : ""; break; } case "NONE": { // const storefrontResultDiscounts = this.addPrefixToProductVariantIds(storefrontResult.discounts); // object const storefrontResultDiscounts = storefrontResult.discounts; // object newAttributeValueData = (isValid(storefrontResultDiscounts) && customDiscountEligibilityResult) ? { customDiscountEligibilityResult, discounts: storefrontResultDiscounts } : ""; break; } } } else { newAttributeValueData = ""; } return newAttributeValueData; } catch (err) { gfg.utility.debugConsole("err inside gfgCustomDiscountFunctionLogicHandlers-init", err); } }, addPrefixToProductVariantIds: function(discountsData) { const prefix = "gid://shopify/ProductVariant/"; try { // Iterate over each discount in the discounts array discountsData.forEach(discount => { // Iterate over each target in the targets array discount.targets.forEach(target => { // Add the prefix to the productVariant id target.productVariant.id = `${prefix}${target.productVariant.id}`; }); }); return discountsData; } catch (error) { gfg.utility.debugConsole("An error occurred while adding prefixes to productVariant IDs:", error); // Optionally, return a default value or rethrow the error return null; // or throw error; } }, getSortedLineItems: function(lineItems, sortBasedOn, sortByOrder) { try { let sortFunction; if (sortBasedOn === "productPrice") { if (sortByOrder === "ascending") { sortFunction = (a, b) => parseFloat(a.price) - parseFloat(b.price); } else if (sortByOrder === "descending") { sortFunction = (a, b) => parseFloat(b.price) - parseFloat(a.price); } } else if (sortBasedOn === "lineItemPrice") { if (sortByOrder === "ascending") { sortFunction = (a, b) => (parseFloat(a.price) * a.quantity) - (parseFloat(b.price) * b.quantity); } else if (sortByOrder === "descending") { sortFunction = (a, b) => (parseFloat(b.price) * b.quantity) - (parseFloat(a.price) * a.quantity); } } return lineItems.sort(sortFunction); } catch (error) { gfg.utility.debugConsole("An error occurred while sorting line items:", error); return lineItems; } }, getEligibleTier: function(line, tiersData, origin) { try { let finalTier; let lineQty = origin == "line" ? line.quantity : line.itemQty; let lineAmt = origin == "line" ? (parseFloat(line.price / 100) * line.quantity) : (line.itemQty * line.itemPrice); for (const tier of tiersData.tiersList) { const tierCondition = parseFloat(tier.tierCondition); if (tiersData.tierType === "INDIVIDUAL_DISCOUNTABLE_ITEMS_QUANTITY" && lineQty >= tierCondition) { finalTier = tier; // Return the tier if the quantity condition is met } else if (tiersData.tierType === "INDIVIDUAL_DISCOUNTABLE_ITEMS_SUBTOTAL" && lineAmt >= tierCondition) { finalTier = tier; // Return the tier if the subtotal condition is met } } return finalTier; // Return null if no valid tier is found } catch(err) { gfg.utility.debugConsole("err inside getEligibleTier", err); } }, prepareTargetsForConditionalDiscount: async function(discountData, cartData) { try { let finalResult = { discounts: [], validVariantIds: [] }; cartData.presentmentCurrencyRate = gfg.utility.getActiveCurrencyRate(); let validVariantIds = []; // Array to hold all valid line items let validLineItems = []; let maxDiscountableItemsPerProduct = parseInt(discountData.discountSettings.maxDiscountableItemsPerProduct); let maxDiscountableProductsPerCart = parseInt(discountData.discountSettings.maxDiscountableProductsPerCart); let totalMaxDiscountableProducts = parseInt(discountData?.discountSettings?.totalMaxDiscountableProducts) || 0; let discountValue = parseFloat(discountData.discountSettings.value); let sortedLineItems = JSON.parse(JSON.stringify(cartData.items)); let totalDiscountedProducts = 0; // To track the total quantity of discounted products let rulesList = discountData.discountSettings.rulesData.rulesGlobalList[0].rulesList; let rulesOperator = discountData.discountSettings.rulesData.rulesGlobalList[0].rulesOperator; if (discountData.discountSettings.useEligibilityProductsForDiscount) { rulesList = discountData.rulesData.rulesGlobalList[0].rulesList; //consider eligibility rules rulesOperator = discountData.rulesData.rulesGlobalList[0].rulesOperator; //consider eligibility rules } if (discountData?.discountSettings?.sortBasedOn != "none" && discountData?.discountSettings?.sortByOrder != "none") { sortedLineItems = gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getSortedLineItems(sortedLineItems, discountData.discountSettings.sortBasedOn, discountData.discountSettings.sortByOrder); } const discountedVariants = new Map(); for (let i = 0; i < sortedLineItems.length; i++) { if (maxDiscountableProductsPerCart != 0 && validLineItems.length == maxDiscountableProductsPerCart) { break; } if (totalMaxDiscountableProducts != 0 && totalDiscountedProducts >= totalMaxDiscountableProducts) { break; } let line = sortedLineItems[i]; let isValidLine = (rulesOperator === "AND"); // Check each rule to see if the line meets all product-related conditions for (let j = 0; j < rulesList.length; j++) { let rule = rulesList[j]; if (rule.type === "DISCOUNT_PRODUCT" || rule.type == "PRODUCT") { let productResult = await gfg.customDiscountValidationFunctions.validateProductRuleForDiscountableItems(cartData, rule, line); // Ensure that the validation result itself is true and the line item is in the validated products if (productResult) { if (rulesOperator == "AND") { isValidLine = true && isValidLine; } else { // "OR" isValidLine = true; } } else { // If the productResult result is false, handle according to the logic operator if (rulesOperator == "AND") { isValidLine = false; // Fail the item immediately for "AND" } // For "OR", no change is needed here, since failing one rule doesn't affect others } } } if(isValidLine) { let quantityToAdd = line.quantity; // Apply maxDiscountableItemsPerProduct if it is not 0 if (maxDiscountableItemsPerProduct > 0) { const alreadyDiscounted = discountedVariants.get(line.variant_id) || 0; const remainingDiscountable = maxDiscountableItemsPerProduct - alreadyDiscounted; quantityToAdd = Math.min(line.quantity, remainingDiscountable); // Update the map with the new discounted quantity discountedVariants.set(line.variant_id, alreadyDiscounted + quantityToAdd); } if (totalMaxDiscountableProducts > 0 && (totalDiscountedProducts + quantityToAdd) > totalMaxDiscountableProducts) { quantityToAdd = totalMaxDiscountableProducts - totalDiscountedProducts; } if (quantityToAdd > 0) { validLineItems.push({ productVariant: { id: line.variant_id, quantity: quantityToAdd }, itemPrice: parseFloat(line.price / 100) // Use line price temporarily for calculation within this function }); totalDiscountedProducts += quantityToAdd; } if(!validVariantIds.includes(line.variant_id)) { validVariantIds.push(line.variant_id) //modify this part to also send the quantity } } } // if(validLineItems.length == 0) { // finalResult.functionResult = []; // } let discounts = []; if (discountData.discountSettings.type == "PERCENTAGE") { validLineItems.forEach(item => delete item.itemPrice); //not needed in this case discounts.push({ value: { percentage: { value: discountValue } }, targets: validLineItems, message: discountData?.discountSettings?.message || discountData.discountTitle }); } else if (discountData.discountSettings.type == "FIXED") { validLineItems.forEach(item => delete item.itemPrice); //not needed in this case discounts.push({ value: { fixedAmount: { amount: discountValue * parseFloat(cartData.presentmentCurrencyRate), appliesToEachItem: discountData.discountSettings.appliesToEachItem, } }, targets: validLineItems, message: discountData?.discountSettings?.message || discountData.discountTitle }); } else if (discountData.discountSettings.type == "FIXED_PRICE") { validLineItems.forEach(item => { discountValue = discountValue * parseFloat(cartData.presentmentCurrencyRate) let discountAmount = item.itemPrice - discountValue; if(discountAmount < 0) { discountAmount = item.itemPrice } let newProductVariant = { ...item.productVariant, quantity: item.productVariant.quantity, } discounts.push({ value: { fixedAmount: { amount: discountAmount, //* parseFloat(cartData.presentmentCurrencyRate), appliesToEachItem: true, } }, targets: [{ productVariant: newProductVariant }], // Create a target with only necessary info message: discountData?.discountSettings?.message || discountData.discountTitle }); }); } else { } finalResult.discounts = discounts; finalResult.validVariantIds = validVariantIds; return finalResult; } catch (err) { gfg.utility.debugConsole("Error in prepareTargetsForConditionalDiscount:", err); return; // Return an empty array in case of an error } }, getValidTieredDiscountValue: function(cartData, discountData, validLineItems) { try { // Extract tier data from discount settings const { tierType, tiersList } = discountData.discountSettings.tiersData; // Initialize the valid tier to null let validTier = null; // Determine the basis for tier comparison let comparisonValue = 0; if (tierType === "CART_QUANTITY") { // Sum the quantities of all items in the cart comparisonValue = cartData.items.reduce((total, line) => total + line.quantity, 0); } else if (tierType === "CART_SUBTOTAL") { // Use the subtotal amount for comparison comparisonValue = gfg.customDiscountValidationFunctions.getAmountOfGivenProducts(cartData.items); } else if (tierType === "DISCOUNTABLE_ITEMS_QUANTITY") { // Sum the quantities of all discountable items comparisonValue = validLineItems.reduce((total, item) => total + item.itemQty, 0); } else if (tierType === "DISCOUNTABLE_ITEMS_SUBTOTAL") { // Sum the total prices of all discountable items //comparisonValue = validLineItems.reduce((total, item) => total + (item.productVariant.quantity * item.itemPrice), 0); comparisonValue = validLineItems.reduce((total, item) => total + (item.itemQty * item.itemPrice), 0); } // Iterate through the tiers list to find the applicable discount tier for (const tier of tiersList) { const tierCondition = parseFloat(tier.tierCondition); // Check if the comparison value meets the tier condition if (comparisonValue >= tierCondition) { // If so, update the valid tier to the current tier validTier = tier; } else { // Stop checking further tiers since they are sorted in ascending order break; } } return validTier; } catch (err) { gfg.utility.debugConsole("err inside getValidTieredDiscountValue", err); return null; // Return null in case of an error } }, prepareTargetsForTieredDiscount: async function(discountData, cartData) { try { let finalResult = { discounts: [], validVariantIds: [] }; cartData.presentmentCurrencyRate = gfg.utility.getActiveCurrencyRate(); let validVariantIds = []; // Array to hold all valid line items let validLineItems = []; let validTier = null; let maxDiscountableItemsPerProduct = parseInt(discountData.discountSettings.maxDiscountableItemsPerProduct); let maxDiscountableProductsPerCart = parseInt(discountData.discountSettings.maxDiscountableProductsPerCart); let totalMaxDiscountableProducts = parseInt(discountData?.discountSettings?.totalMaxDiscountableProducts) || 0; let sortedLineItems = JSON.parse(JSON.stringify(cartData.items)); let totalDiscountedProducts = 0; // To track the total quantity of discounted products let rulesList = discountData.discountSettings.rulesData.rulesGlobalList[0].rulesList; let rulesOperator = discountData.discountSettings.rulesData.rulesGlobalList[0].rulesOperator; if (discountData.discountSettings.useEligibilityProductsForDiscount) { rulesList = discountData.rulesData.rulesGlobalList[0].rulesList; //consider eligibility rules rulesOperator = discountData.rulesData.rulesGlobalList[0].rulesOperator; //consider eligibility rules } if (discountData?.discountSettings?.sortBasedOn != "none" && discountData?.discountSettings?.sortByOrder != "none") { sortedLineItems = gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getSortedLineItems(sortedLineItems, discountData.discountSettings.sortBasedOn, discountData.discountSettings.sortByOrder); } const discountedVariants = new Map(); for (let i = 0; i < sortedLineItems.length; i++) { if (maxDiscountableProductsPerCart != 0 && validLineItems.length == maxDiscountableProductsPerCart) { break; } if (totalMaxDiscountableProducts != 0 && totalDiscountedProducts >= totalMaxDiscountableProducts) { break; } let line = sortedLineItems[i]; let isValidLine = (rulesOperator === "AND"); // Check each rule to see if the line meets all product-related conditions for (let j = 0; j < rulesList.length; j++) { let rule = rulesList[j]; if (rule.type === "DISCOUNT_PRODUCT" || rule.type == "PRODUCT") { let productResult = await gfg.customDiscountValidationFunctions.validateProductRuleForDiscountableItems(cartData, rule, line); // Ensure that the validation result itself is true and the line item is in the validated products if (productResult) { if (rulesOperator == "AND") { isValidLine = true && isValidLine; } else { // "OR" isValidLine = true; } } else { // If the productResult result is false, handle according to the logic operator if (rulesOperator == "AND") { isValidLine = false; // Fail the item immediately for "AND" } // For "OR", no change is needed here, since failing one rule doesn't affect others } } } if (isValidLine && (discountData.discountSettings.tiersData.tierType === "INDIVIDUAL_DISCOUNTABLE_ITEMS_QUANTITY" || discountData.discountSettings.tiersData.tierType === "INDIVIDUAL_DISCOUNTABLE_ITEMS_SUBTOTAL")) { let origin = "line"; validTier = this.getEligibleTier(line, discountData.discountSettings.tiersData, origin); isValidLine = !!validTier; // isValidLine is true if a valid tier is found } if(isValidLine) { let quantityToAdd = JSON.parse(JSON.stringify(line.quantity)); // Apply maxDiscountableItemsPerProduct if it is not 0 if (maxDiscountableItemsPerProduct > 0) { const alreadyDiscounted = discountedVariants.get(line.variant_id) || 0; const remainingDiscountable = maxDiscountableItemsPerProduct - alreadyDiscounted; quantityToAdd = Math.min(line.quantity, remainingDiscountable); // Update the map with the new discounted quantity discountedVariants.set(line.variant_id, alreadyDiscounted + quantityToAdd); } if (totalMaxDiscountableProducts > 0 && (totalDiscountedProducts + quantityToAdd) > totalMaxDiscountableProducts) { quantityToAdd = totalMaxDiscountableProducts - totalDiscountedProducts; } if (quantityToAdd > 0) { validLineItems.push({ productVariant: { id: line.variant_id, quantity: quantityToAdd }, itemPrice: parseFloat(line.price / 100), // Use line price temporarily for calculation within this function itemQty: line.quantity, // Use line price temporarily for calculation within this function }); totalDiscountedProducts += quantityToAdd; } if(!validVariantIds.includes(line.variant_id)) { validVariantIds.push(line.variant_id) //modify this part to also send the quantity } } } // if(validLineItems.length == 0) { // finalResult.functionResult = []; // } let discounts = gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getDiscountsArrayForTieredDiscounts(discountData, cartData, validLineItems); finalResult.discounts = discounts; finalResult.validVariantIds = validVariantIds; return finalResult; } catch (err) { gfg.utility.debugConsole("Error in prepareTargetsForConditionalDiscount:", err); return; // Return an empty array in case of an error } }, getDiscountsArrayForTieredDiscounts: function (discountData, cartData, validLineItems) { try { let validTier = gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getValidTieredDiscountValue(cartData, discountData, validLineItems); let appliesToEachItem = validTier?.appliesToEachItem; let discounts = []; if (discountData.discountSettings.type === "FIXED_PRICE") { let discountValue = parseFloat(validTier.discountValue) * parseFloat(cartData.presentmentCurrencyRate); let totalQty = 0; let finalPriceAfterDiscount = 0; if (!appliesToEachItem) { totalQty = validLineItems.reduce((sum, item) => sum + item.productVariant.quantity, 0); finalPriceAfterDiscount = +(discountValue / totalQty).toFixed(2); } for (let item of validLineItems) { let discountAmount = item.itemPrice - (appliesToEachItem ? discountValue : finalPriceAfterDiscount); if (discountAmount < 0) { discountAmount = item.itemPrice; } let newProductVariant = { ...item.productVariant, quantity: item.productVariant.quantity, }; discounts.push({ value: { fixedAmount: { amount: discountAmount, appliesToEachItem: true, }, }, targets: [{ productVariant: newProductVariant }], message: validTier?.discountMessage || discountData.discountTitle, }); } return discounts; } if (discountData?.discountSettings?.tiersData?.tierType === "INDIVIDUAL_DISCOUNTABLE_ITEMS_QUANTITY" || discountData?.discountSettings?.tiersData?.tierType === "INDIVIDUAL_DISCOUNTABLE_ITEMS_SUBTOTAL") { for (let item of validLineItems) { validTier = this.getEligibleTier(item, discountData.discountSettings.tiersData); const discountObject = { targets: [{ productVariant: { id: item.productVariant.id, quantity: item.productVariant.quantity } }], message: validTier?.discountMessage || discountData.discountTitle } if (validTier) { let discountValue = validTier.discountValue; if (discountData.discountSettings.type === "PERCENTAGE") { discountObject.value = { percentage: { value: discountValue } }; discounts.push(discountObject); } else if (discountData.discountSettings.type === "FIXED") { discountObject.value = { fixedAmount: { amount: discountValue * parseFloat(cartData.presentmentCurrencyRate), appliesToEachItem: validTier.appliesToEachItem, } }; discounts.push(discountObject); } } } return discounts; } else { if (!validTier) { return []; } let discountValue = validTier.discountValue; validLineItems.forEach(item => { delete item.itemPrice; delete item.itemQty; }); const discountObject = { targets: validLineItems, message: validTier?.discountMessage || discountData.discountTitle } if (discountData.discountSettings.type === "PERCENTAGE") { discountObject.value = { percentage: { value: discountValue } }; discounts.push(discountObject); } else if (discountData.discountSettings.type === "FIXED") { discountObject.value = { fixedAmount: { amount: discountValue * parseFloat(cartData.presentmentCurrencyRate), appliesToEachItem: validTier.appliesToEachItem, } }; discounts.push(discountObject); } return discounts; } } catch(err) { gfg.utility.debugConsole("Error in getDiscountsArrayForTieredDiscounts", err); } }, checkTargetForValidQualifierEligibility: function (qualifierType, lineItemData, ruleObject) { try { if(qualifierType === "individualDiscountableItemsQuantity") { return gfg.customDiscountValidationFunctions.compareMetrics(ruleObject, lineItemData.quantity); } if(qualifierType === "individualDiscountableItemsSubtotal") { return gfg.customDiscountValidationFunctions.compareMetrics(ruleObject, lineItemData.subtotal); } return true; } catch(err) { console.error("Error in checkTargetForValidQualifierEligibility", err); } }, checkTargetCondition: function(lineQtyForDiscount, targets, ruleValue, discountData) { try { if((!ruleValue.maxDiscountableProductsPerCart || ruleValue.maxDiscountableProductsPerCart == "0") && lineQtyForDiscount > 0) { return true; } else if(lineQtyForDiscount > 0 && targets.length < ruleValue.maxDiscountableProductsPerCart) { return true; } else { return false; } // if(gfg.settings.merchantInfo.shopName != "infused-drinks.myshopify.com") { // if(lineQtyForDiscount > 0) { // return true; // } // return false; // } else if (gfg.settings.merchantInfo.shopName == "infused-drinks.myshopify.com") { // if(lineQtyForDiscount > 0 && targets.length < ruleValue.maxDiscountableProductsPerCart) { // return true; // } else { // return false; // } // } } catch(err) { gfg.utility.debugConsole(err); } }, prepareTargetsForTieredGiftDiscount: async function(discountData, cartData) { try { let finalResult = { discounts: [], validVariantIds: {} }; let validItemsData = {}; cartData.presentmentCurrencyRate = gfg.utility.getActiveCurrencyRate(); const tiers = discountData.discountSettings.rulesData.rulesGlobalList[0].rulesList; const discountSettings = discountData.discountSettings; let discountApplicationStrategy = discountSettings.rulesData.rulesGlobalList[0].discountApplicationStrategy; let discounts = []; let firstSatisfiedTier = null; let maxSatisfiedTier = null; const cartQuantity = gfg.customDiscountValidationFunctions.getQtyOfGivenProducts(cartData.items); const cartSubtotal = gfg.customDiscountValidationFunctions.getAmountOfGivenProducts(cartData.items); for (let tierIndex = 0; tierIndex < tiers.length; tierIndex++) { const tier = tiers[tierIndex]; const ruleValue = tier.ruleValue; const qualifierType = ruleValue.qualifierType; let targets = []; let totalQuantity = 0; let totalAmount = 0; let totalQtyOfProductsDiscounted = 0; let allDiscountableItems = []; const discountedVariants = new Map(); const processedValidItems = new Map(); const totalMaxDiscountableProducts = ruleValue?.totalMaxDiscountableProducts || 0; let sortedLineItems = JSON.parse(JSON.stringify(cartData.items)); if(tier?.ruleValue?.sortBasedOn != "none" && tier?.ruleValue?.sortByOrder != "none") { sortedLineItems = gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getSortedLineItems(sortedLineItems, tier.ruleValue.sortBasedOn, tier.ruleValue.sortByOrder) } for (let i = 0; i < sortedLineItems.length; i++) { const line = sortedLineItems[i]; let lineQuantity = line.quantity; const lineAmount = parseFloat(lineQuantity * (line.price / 100)) //parseFloat(line.cost.amountPerQuantity.amount) * lineQuantity; // Check if the line item meets the product-related conditions const productResult = await gfg.customDiscountValidationFunctions.validateProductRuleForDiscountableItems(cartData, tier, line); if (productResult) { allDiscountableItems.push(line); const itemQuantity = processedValidItems.get(line.variant_id)?.quantity || 0; const itemSubtotal = processedValidItems.get(line.variant_id)?.subtotal || 0; processedValidItems.set(line.variant_id, { quantity: itemQuantity + lineQuantity, subtotal: itemSubtotal + lineAmount }); if(!this.checkTargetForValidQualifierEligibility(qualifierType, processedValidItems.get(line.variant_id), tier)) { continue; } let lineQtyForDiscount = lineQuantity; if (ruleValue.maxDiscountableItemsPerProduct > 0) { const alreadyDiscounted = discountedVariants.get(line.variant_id) || 0; const remainingDiscountable = ruleValue.maxDiscountableItemsPerProduct - alreadyDiscounted; lineQtyForDiscount = Math.min(lineQuantity, remainingDiscountable); // Update the map with the new discounted quantity discountedVariants.set(line.variant_id, alreadyDiscounted + lineQtyForDiscount); } if (totalMaxDiscountableProducts > 0 && (totalQtyOfProductsDiscounted + lineQtyForDiscount) > totalMaxDiscountableProducts) { lineQtyForDiscount = totalMaxDiscountableProducts - totalQtyOfProductsDiscounted; } if(this.checkTargetCondition(lineQtyForDiscount, targets, ruleValue, discountData)) { // if (lineQtyForDiscount > 0) { targets.push({ productVariant: { id: line.variant_id, quantity: lineQtyForDiscount }, itemPrice: parseFloat(line.price / 100), itemQty: line.quantity, }); totalQtyOfProductsDiscounted += lineQtyForDiscount if(validItemsData?.[`tierIndex${tierIndex}`]) { if(!validItemsData?.[`tierIndex${tierIndex}`]?.includes(line.variant_id)) { validItemsData[`tierIndex${tierIndex}`].push(line.variant_id); } } else { validItemsData[`tierIndex${tierIndex}`] = [line.variant_id] } if (qualifierType === "discountableItemsQuantity") { totalQuantity += lineQuantity; } else if (qualifierType === "discountableItemsSubtotal") { totalAmount += lineAmount; } } if (ruleValue.maxDiscountableProductsPerCart > 0 && targets.length >= ruleValue.maxDiscountableProductsPerCart && qualifierType != "discountableItemsQuantity" && qualifierType != "discountableItemsSubtotal") { break; } } } if(targets.length == 0) { continue; } let isValid = false; if (qualifierType === "cartQuantity") { isValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, cartQuantity); } else if (qualifierType === "discountableItemsQuantity") { let _discountableItemsQuantity = 0; allDiscountableItems.forEach((ele) => { _discountableItemsQuantity += ele.quantity }) isValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, _discountableItemsQuantity); //isValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, totalQuantity); } else if (qualifierType === "cartSubtotal") { isValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, cartSubtotal); } else if (qualifierType === "discountableItemsSubtotal") { let _discountableItemsSubtotal = gfg.customDiscountValidationFunctions.getAmountOfGivenProducts(allDiscountableItems); tier.ruleValue.qualifierValue = tier.ruleValue.qualifierValue * cartData.presentmentCurrencyRate; isValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, _discountableItemsSubtotal); //isValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, totalAmount); } else if (qualifierType === "individualDiscountableItemsSubtotal") { const itemsMap = {}; allDiscountableItems.forEach((ele) => { const itemId = ele.variant_id; const itemPrice = parseFloat(ele.price / 100); const itemQty = ele.quantity; itemsMap[itemId] = itemsMap[itemId] ? itemsMap[itemId] + itemPrice * itemQty : itemPrice * itemQty; }) allDiscountableItems.forEach((item) => { const _individualDiscountableItemsSubtotal = itemsMap[item.variant_id]; const individualLineItemIsValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, _individualDiscountableItemsSubtotal); isValid = isValid || individualLineItemIsValid; }) } else if (qualifierType === "individualDiscountableItemsQuantity") { const itemsMap = {}; allDiscountableItems.forEach((ele) => { const itemId = ele.variant_id; const itemQty = ele.quantity; itemsMap[itemId] = itemsMap[itemId] ? itemsMap[itemId] + itemQty : itemQty; }) allDiscountableItems.forEach((item) => { const _individualDiscountableItemsQuantity = itemsMap[item.variant_id]; const individualLineItemIsValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, _individualDiscountableItemsQuantity); isValid = isValid || individualLineItemIsValid; }) } else if (qualifierType === "differentDiscountableItemsQuantity") { const itemsSet = new Set(); allDiscountableItems.forEach((item) => { itemsSet.add(item.variant_id); }) const _differentDiscountableItemsQuantity = itemsSet.size; isValid = gfg.customDiscountValidationFunctions.compareMetrics(tier, _differentDiscountableItemsQuantity); } if (isValid) { if (discountSettings.type == "FIXED") { targets.forEach(item => { delete item.itemPrice; delete item.itemQty; }); discounts.push({ value: { fixedAmount: { amount: parseFloat(ruleValue.discountValue), appliesToEachItem: ruleValue.discountAppliesToEachItem } }, targets: targets, message: ruleValue.message || discountData.discountTitle }); } else if (discountSettings.type == "PERCENTAGE") { targets.forEach(item => { delete item.itemPrice; delete item.itemQty; }); discounts.push({ value: { percentage: { value: parseFloat(ruleValue.discountValue), } }, targets: targets, message: ruleValue.message || discountData.discountTitle }); } else if (discountSettings.type === "FIXED_PRICE") { let totalQty = 0; let finalPriceAfterDiscount = 0; let discountValue = parseFloat(ruleValue.discountValue) * parseFloat(cartData.presentmentCurrencyRate); let appliesToEachItem = ruleValue.discountAppliesToEachItem; if(!appliesToEachItem) { totalQty = targets.reduce((sum, item) => sum + item.productVariant.quantity, 0); finalPriceAfterDiscount = +(discountValue / totalQty).toFixed(2); } targets.forEach(item => { const originalPrice = parseFloat(item.itemPrice); let discountAmount = originalPrice - (appliesToEachItem ? discountValue : finalPriceAfterDiscount); if(discountAmount < 0) { discountAmount = originalPrice; } delete item.itemPrice; delete item.itemQty; discounts.push({ value: { fixedAmount: { amount: discountAmount, appliesToEachItem: true, }, }, targets: [item], message: ruleValue.message || discountData.discountTitle, tierIndex: tierIndex, }); }); if (firstSatisfiedTier === null || tierIndex < firstSatisfiedTier) { firstSatisfiedTier = tierIndex; } if (!maxSatisfiedTier || parseFloat(tier.ruleValue.discountValue) > parseFloat(tiers[maxSatisfiedTier].ruleValue.discountValue)) { maxSatisfiedTier = tierIndex; } } } } if (discountSettings.type === "FIXED_PRICE") { if (discountApplicationStrategy === 'FIRST' && firstSatisfiedTier !== null) { discounts = discounts.filter(discount => discount.tierIndex === firstSatisfiedTier); } else if (discountApplicationStrategy === 'MAXIMUM' && maxSatisfiedTier !== null) { discounts = discounts.filter(discount => discount.tierIndex === maxSatisfiedTier); } } discounts.forEach(discount => delete discount?.tierIndex); finalResult.discounts = discounts; finalResult.validVariantIds = validItemsData; return finalResult; } catch (err) { gfg.utility.debugConsole("Error in evaluateAndReturnTieredGiftsDiscounts:", err); return; // Return an empty discount in case of an error } }, prepareTargetsForBuyXGetYDiscount: async function(discountData, cartData) { try { const validVariantIds = {}; const discounts = []; cartData.presentmentCurrencyRate = gfg.utility.getActiveCurrencyRate(); // Set currency rate const tiers = discountData.discountSettings.rulesData.rulesGlobalList[0].rulesList; const discountSettings = discountData.discountSettings; const discountApplicationStrategy = discountSettings.rulesData.rulesGlobalList[0].discountApplicationStrategy; for (let tierIndex = 0; tierIndex < tiers.length; tierIndex++) { const tier = tiers[tierIndex]; const buyXRule = tier.buyXRule; const getYRule = tier.getYRule; const sortBasedOn = tier.sortBasedOn; const sortByOrder = tier.sortByOrder; let totalBuyXQuantity = 0; let totalGetYQuantity = 0; const buyXTargetsMap = new Map(); const getYTargetsMap = new Map(); let getYItemsToProcess = cartData.items; if (sortBasedOn && sortBasedOn !== "none" && sortByOrder && sortByOrder !== "none") { getYItemsToProcess = gfgCustomDiscount.gfgCustomDiscountFunctionLogicHandlers.getSortedLineItems(getYItemsToProcess, sortBasedOn, sortByOrder); } // Loop through cart lines to find qualifying Buy X items for (let i = 0; i < cartData.items.length; i++) { const line = cartData.items[i]; let lineQuantity = line.quantity; // Check if the line item meets the Buy X conditions const buyXResult = await gfg.customDiscountValidationFunctions.validateProductRuleForDiscountableItems(cartData, buyXRule, line); if (buyXResult) { totalBuyXQuantity += lineQuantity; if (!validVariantIds[`tierIndex${tierIndex}`]) { validVariantIds[`tierIndex${tierIndex}`] = { bXT: [line.variant_id] }; } else { const bXT = validVariantIds[`tierIndex${tierIndex}`]?.bXT; if (bXT) { if (!bXT.includes(line.variant_id)) { bXT.push(line.variant_id); } } else { validVariantIds[`tierIndex${tierIndex}`].bXT = [line.variant_id]; } } buyXTargetsMap.set( line.variant_id, (buyXTargetsMap.get(line.variant_id) || 0) + lineQuantity ); } } const buyXItemCount = parseInt(buyXRule.itemCount); const getYItemCount = parseInt(getYRule.itemCount); const maxDiscountableSets = parseInt(tier.maxDiscountableSets); const adminDiscountableSets = Math.floor(totalBuyXQuantity / buyXItemCount); let cartDiscountableSets = Math.min(adminDiscountableSets, maxDiscountableSets); // If there are no actual sets possible if (cartDiscountableSets <= 0) { //updating if the BuyX condition was fulfilled for the specific discount gfgCustomDiscount.state.BuyXConditionFulfilled[discountData.title] = false; //exclusive for storefront continue; } gfgCustomDiscount.state.BuyXConditionFulfilled[discountData.title] = true; //exclusive for storefront // Loop through cart lines to find qualifying Get Y items for (let i = 0; i < getYItemsToProcess.length; i++) { const line = getYItemsToProcess[i]; let lineQuantity = line.quantity; const getYResult = await gfg.customDiscountValidationFunctions.validateProductRuleForDiscountableItems(cartData, getYRule, line); if (getYResult) { totalGetYQuantity += lineQuantity; if (!validVariantIds[`tierIndex${tierIndex}`]) { validVariantIds[`tierIndex${tierIndex}`] = { gYT: [line.variant_id] }; } else { const gYT = validVariantIds[`tierIndex${tierIndex}`]?.gYT; if (gYT) { if (!gYT.includes(line.variant_id)) { gYT.push(line.variant_id); } } else { validVariantIds[`tierIndex${tierIndex}`].gYT = [line.variant_id]; } } getYTargetsMap.set( line.variant_id, (getYTargetsMap.get(line.variant_id) || 0) + lineQuantity ); } } // Calculate discount based on cartDiscountableSets and the quantity needed for Get Y let totalGetYQuantityNeeded = cartDiscountableSets * getYItemCount; const commonProducts = new Map([...buyXTargetsMap].filter(([buyXId, buyXQty]) => getYTargetsMap.has(buyXId) && getYTargetsMap.get(buyXId) === buyXQty)); const commonProductsQty = Array.from(commonProducts.values()).reduce((acc, qty) => acc + qty, 0); const featureDate = new Date("Mon Nov 18 2024 17:00:00 GMT+0530 (India Standard Time)").getTime(); const discountDate = new Date(discountData.createdAt).getTime(); //using createdAt instead of discountCreatedAt as we are using db data //we deployed fix for buyX getX scenario on 11/18/2024, so if discount is created before this date, then we won't run the logic below if(discountDate && discountDate > featureDate && commonProductsQty > 0) { let noOfSetsMadeWhenCommonProductsArePresent = 0; const diffBuyXQty = totalBuyXQuantity - commonProductsQty; const diffGetYQty = totalGetYQuantity - commonProductsQty; const diffProductsSets = Math.floor(Math.min(diffBuyXQty / buyXItemCount, diffGetYQty / getYItemCount)); const commonProductsSets = Math.floor(commonProductsQty / (buyXItemCount + getYItemCount)); noOfSetsMadeWhenCommonProductsArePresent = commonProductsSets + diffProductsSets; noOfSetsMadeWhenCommonProductsArePresent = Math.min(noOfSetsMadeWhenCommonProductsArePresent, maxDiscountableSets); totalGetYQuantityNeeded = getYItemCount * noOfSetsMadeWhenCommonProductsArePresent; //if no. of max sets are not reached, then we need to check if we can make more sets with the remaining common products if(noOfSetsMadeWhenCommonProductsArePresent < maxDiscountableSets) { const remainingCommonProductsQty = commonProductsQty - commonProductsSets * (buyXItemCount + getYItemCount); const remainingDiffProductXQty = totalBuyXQuantity - diffProductsSets * buyXItemCount - commonProductsQty; const remainingDiffProductYQty = totalGetYQuantity - diffProductsSets * getYItemCount - commonProductsQty; if (remainingDiffProductXQty + remainingCommonProductsQty >= buyXItemCount) { const commonProductsRemainingAfterMakingBuyXSet = remainingCommonProductsQty - (buyXItemCount - remainingDiffProductXQty); totalGetYQuantityNeeded += commonProductsRemainingAfterMakingBuyXSet; noOfSetsMadeWhenCommonProductsArePresent++; cartDiscountableSets = noOfSetsMadeWhenCommonProductsArePresent; // exclusive for storefront } } } gfgCustomDiscount.state.allowedGetYQuantity[discountData.title] = cartDiscountableSets * getYItemCount; // exclusive for storefront let remainingGetYQuantityToDiscount = Math.min(totalGetYQuantityNeeded, totalGetYQuantity); const targets = []; //reversing the map to apply the discount on the items that were added first let reversedGetYTargetsMap = getYTargetsMap; if(!sortBasedOn || sortBasedOn == "none" || !sortByOrder || sortByOrder == "none") { reversedGetYTargetsMap = new Map([...getYTargetsMap].reverse()); } for (const [productId, productQty] of reversedGetYTargetsMap.entries()) { if (remainingGetYQuantityToDiscount <= 0) { break; } const quantityToDiscount = Math.min(productQty, remainingGetYQuantityToDiscount); targets.push({ productVariant: { id: productId, quantity: quantityToDiscount, }, }); remainingGetYQuantityToDiscount -= quantityToDiscount; } if (targets.length > 0) { if (discountSettings.type == "FIXED") { discounts.push({ value: { fixedAmount: { amount: parseFloat(tier.discountValue), appliesToEachItem: tier.appliesToEachItem, }, }, targets: targets, message: tier.message || discountData.discountTitle, }); } else if (discountSettings.type == "PERCENTAGE") { discounts.push({ value: { percentage: { value: parseFloat(tier.discountValue), }, }, targets: targets, message: tier.message || discountData.discountTitle, }); } else if (discountSettings.type === "FIXED_PRICE") { const lineItemsMap = new Map(); cartData.items.forEach(line => { lineItemsMap.set(line.variant_id, line); }); let totalQty = 0; let finalPriceAfterDiscount = 0; let discountValue = parseFloat(tier.discountValue) * parseFloat(cartData.presentmentCurrencyRate); let appliesToEachItem = tier.appliesToEachItem; //here we will calculate the final price after discount based on the discountable items quantity for the case where discount is not applied to each item if (!appliesToEachItem) { totalQty = targets.reduce((sum, item) => sum + item.productVariant.quantity, 0); finalPriceAfterDiscount = +(discountValue / totalQty).toFixed(2); } targets.forEach(item => { const itemData = lineItemsMap.get(item.productVariant.id); const itemPrice = itemData.price / 100; let discountAmount = itemPrice - (appliesToEachItem ? discountValue : finalPriceAfterDiscount); if (discountAmount < 0) { discountAmount = itemPrice; } discounts.push({ value: { fixedAmount: { amount: discountAmount, appliesToEachItem: true, }, }, targets: [{ productVariant: item.productVariant }], message: discountData?.discountSettings?.message || discountData.discountTitle, }); }); } } } return { validVariantIds, discounts }; // Return both valid items data and the discounts array } catch (err) { gfg.utility.debugConsole("Error in prepareTargetsForBuyXGetYDiscount:", err); return { validVariantIds: {}, discounts: [] }; // Return empty objects in case of an error } }, toCustomBase64: function(data) { try { let customBase64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; let output = ''; let buffer = 0; let bitsInBuffer = 0; for (let i = 0; i < data.length; i++) { buffer = (buffer << 8) | data.charCodeAt(i); bitsInBuffer += 8; while (bitsInBuffer >= 6) { const index = (buffer >> (bitsInBuffer - 6)) & 0x3f; output += customBase64Alphabet[index]; bitsInBuffer -= 6; } } if (bitsInBuffer > 0) { const index = (buffer << (6 - bitsInBuffer)) & 0x3f; output += customBase64Alphabet[index]; } return output; } catch(err) { gfg.utility.debugConsole("err inside toCustomBase64", err); } }, fromCustomBase64: function(data) { try { let customBase64Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; let output = ''; let buffer = 0; let bitsInBuffer = 0; for (let i = 0; i < data.length; i++) { const index = customBase64Alphabet.indexOf(data[i]); if (index === -1) { throw new Error('Invalid character in custom base64 data'); } buffer = (buffer << 6) | index; bitsInBuffer += 6; if (bitsInBuffer >= 8) { const charCode = (buffer >> (bitsInBuffer - 8)) & 0xff; output += String.fromCharCode(charCode); bitsInBuffer -= 8; } } return output; } catch(err) { gfg.utility.debugConsole("err inside fromCustomBase64", err); } }, encryptData: function(data, key) { try { let result = ''; for (let i = 0; i < data.length; i++) { // XOR each character with the key character result += String.fromCharCode(data.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } // Convert the result to custom Base64 to make it shorter result = this.toCustomBase64(result); return result; } catch(err) { gfg.utility.debugConsole("err inside encryptData fn", err); } }, decryptData: function(data, key) { try { // Convert from custom Base64 to string let decodedData = this.fromCustomBase64(data); let result = ''; for (let i = 0; i < decodedData.length; i++) { // XOR each character with the key character result += String.fromCharCode(decodedData.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } return result; } catch(err) { gfg.utility.debugConsole("err inside decryptData fn", err); } }, getExistingCartAttributes: function (cartData) { try { const customDiscountKey = "_kite_cfData"; let currentCartAttributes = cartData.attributes; let existingData = currentCartAttributes[customDiscountKey]; // Decrypt and parse the existing data if necessary return existingData ? JSON.parse(existingData) : {}; } catch(err) { gfg.utility.debugConsole("err inside getExistingCartAttributes", err); } }, updateCartAttributesWithCFData: async function (newAttrData) { try { let attributeValue = ""; if (Object.keys(newAttrData).length > 0) { attributeValue = JSON.stringify(newAttrData); } const customDiscountKey = "_kite_cfData"; const dataToBeUpdated = { attributes: { [customDiscountKey]: attributeValue || "", }, }; await gfg.utility.updateCart(dataToBeUpdated); const shopName = window.Shopify.shop; if(shopName == "myriadbeautystore.myshopify.com" || shopName == "pos-ui-extension-testing3.myshopify.com") { gfg.gfgFreeGift.f.updateCartState(); } } catch(err) { gfg.utility.debugConsole("err inside updateCartAttributesWithCFData", err); } } }, gfgBXGY: { state:{ isFirstRenderForCustomBXGY: true, }, init: async function (discountTitle) { try { const customDiscounts = gfg.settings.customDiscount; const customDiscount = customDiscounts.find((discountData) => discountData.title === discountTitle) const page_type = gfg.f.getPageType(); if (page_type === "product") { gfgCustomDiscount.gfgBXGY.initialize(customDiscount, "PRODUCT_PAGE"); } if (page_type === "cart") { gfgCustomDiscount.gfgBXGY.initialize(customDiscount, "CART_PAGE"); } if (page_type !== "cart") { gfgCustomDiscount.gfgBXGY.initialize(customDiscount, "SIDE_CART"); } } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount gfgBXGY init fn", error); } }, initialize: async function (customDiscount, pageType) { try { await gfgCustomDiscount?.gfgBXGY?.f?.getDiscountEligibleProducts(customDiscount); const shouldRenderWidget = await gfgCustomDiscount.gfgBXGY.f.checkIfWidgetShouldBeVisible(customDiscount, pageType) if(!shouldRenderWidget) { //don't render the widget or remove it if it already exist gfgCustomDiscount.gfgBXGY.f.removeWidgetIfAlreadyExists(customDiscount, pageType); return; } console.time("timer-x"); const isBuyXGetYWidgetDisabled = await gfgCustomDiscount.gfgBXGY.f.checkIfGetYProductsCanBeAddedToCart(customDiscount); customDiscount.isBuyXGetYWidgetDisabled = isBuyXGetYWidgetDisabled; console.timeEnd("timer-x"); let gfgPreparedCustomDiscountWidget = gfgCustomDiscount.gfgBXGY.f.prepareUI(customDiscount, pageType); const preparedBXGYWidgetUI = [{ generatedUI: gfgPreparedCustomDiscountWidget, offer: customDiscount }]; gfgCustomDiscount.f.insertIntoPageWrapper(preparedBXGYWidgetUI, pageType); gfgCustomDiscount.gfgBXGY.actions.registerActions(pageType) } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount gfgBXGY initialize fn", error); } }, f:{ getDiscountEligibleProducts : async function (discountData) { try { if(discountData.discountFunctionType == "BUYX_GETY_DISCOUNT") { const getYRule = discountData?.discountSettings?.rulesData?.rulesGlobalList?.[0]?.rulesList?.[0]?.getYRule; const {ruleType, ruleValue} = getYRule; const compatibleRuleTypes = ["products", "collections", "productVariants", "productTags", "productTypes"] if(!compatibleRuleTypes.includes(ruleType) || ruleValue?.operatorType != "include") { return; } let getYProductData; if(ruleType === "products" || ruleType === "productVariants") { const productIds = ruleValue?.products?.map((product => product.productId)) getYProductData = await gfg.customDiscountValidationFunctions.getProductsDataByProductIds(productIds, 5); if(ruleType === "productVariants" ) { for(let i = 0; i < getYProductData.length; i++) { const productFromAdmin = ruleValue?.products[i]; const productForStoreFront = getYProductData[i]; const validVariantIds = productFromAdmin?.variants?.map((variant) => variant?.variantGraphqlId) productForStoreFront.variants = productForStoreFront.variants.filter((variant) => validVariantIds.includes(variant.id)); getYProductData[i] = productForStoreFront; } } gfg.utility.debugConsole("all products data for BuyXGetY", getYProductData) } if(ruleType === "collections") { const collectionsIds = ruleValue?.collections?.map((collection => collection.id)) getYProductData = await gfg.customDiscountValidationFunctions.getProductsDataFromCollectionIds(collectionsIds, 5); gfg.utility.debugConsole("all products data from collection for BuyXGetY", getYProductData) } if(ruleType === "productTags") { const tags = ruleValue?.tags; getYProductData = await gfg.customDiscountValidationFunctions.getProductsDataByProductTags(tags, 5); gfg.utility.debugConsole("all products data from productTags for BuyXGetY", getYProductData); } if(ruleType === "productTypes") { const types = ruleValue?.types; getYProductData = await gfg.customDiscountValidationFunctions.getProductsDataByProductTypes(types, 5); // gfg.utility.debugConsole("all products data from productTypes for BuyXGetY", getYProductData); gfg.utility.debugConsole("all products data from productTypes for BuyXGetY", getYProductData); } //this will filter the undefined products data from going forward getYProductData = getYProductData.filter((product) => product) gfgCustomDiscount.state.productsDataForBuyXGetY[discountData?.title] = getYProductData; } } catch (error) { gfg.utility.debugConsole("error inside gfgBXGY getDiscountEligibleProducts", error); } }, getVariantPrices: function (productData, discountSettings, variantId) { try { const currencySymbol = gfg.utility.getCurrencySymbol() const { discountType, discountValue } = discountSettings; const variantData = productData?.variants?.find((variant) => variant.id === variantId) let variantPrice = variantData?.price?.amount; let variantDiscountedPrice; if(discountType == "PERCENTAGE") { variantDiscountedPrice = (Number(variantPrice) - (Number(variantPrice) * Number(discountValue) / 100)).toFixed(2); } if(discountType == "FIXED") { variantDiscountedPrice = Math.max((Number(variantPrice) - Number(discountValue)).toFixed(2), 0.00); } if(discountType == "FIXED_PRICE") { variantDiscountedPrice = Math.min(Number(variantPrice), Number(discountValue)).toFixed(2); } return { variantPrice: `${currencySymbol}${variantPrice}`, variantDiscountedPrice : `${currencySymbol}${variantDiscountedPrice}` }; } catch (error) { gfg.utility.debugConsole("error inside gfgBXGY getVariantPrices", error); } }, getProductDataFromIndex: function (event, productIndex, pageType) { try { const discountTitle = event?.target?.closest?.(".gfgBuyXGetYCustomDiscountParentContainer")?.id.replace(`_${pageType}`, ""); const productsList = gfgCustomDiscount?.state?.productsDataForBuyXGetY[discountTitle]; return productsList[productIndex]; } catch (error) { gfg.utility.debugConsole("error inside gfgBXGY getProductDataFromIndex", error); } }, removeWidgetIfAlreadyExists: (customDiscount, pageType) => { try { const discountWidget = document.getElementById(`${customDiscount?.title}_${pageType}`); if(discountWidget) { discountWidget.remove(); } } catch (error) { gfg.utility.debugConsole("Error inside gfgBXGY removeWidgetIfAlreadyExists", error); } }, addGetYEligibleItemToCart: async function (variantId, discountDetails) { try { const items = []; const lineItemProperties = { _rule_id: discountDetails.discountFunctionType, _kite_promo_name : discountDetails?.discountTitle } items.push({ id: variantId, quantity: 1, properties: lineItemProperties }) let customDiscountProductData = await gfg.utility.addToCartV2({ items: items }) if(customDiscountProductData){ gfg.utility.BXGYActionsAfterAddToCart(); gfgCustomDiscount.gfgBXGY.init(discountDetails?.discountTitle); return true } return false } catch (error) { gfg.utility.debugConsole("Error inside gfgBXGY addGetYEligibleItemToCart", error); } }, checkIfGetYProductsCanBeAddedToCart: async function (customDiscount) { try { // const cartData = await gfg.utility.getCart(); let cartData; if(gfgCustomDiscount.gfgBXGY.state?.isFirstRenderForCustomBXGY) { cartData = await gfg.utility.getFirstRenderCartData(); gfg.state.cartData = cartData; gfgCustomDiscount.gfgBXGY.state.isFirstRenderForCustomBXGY = false; } else { cartData = await gfg.utility.getCart(); gfg.state.cartData = cartData; } const cartItems = cartData?.items || []; if(cartItems.length == 0) { return true; } let cartItemsWithDiscount = 0; const allowedGetYQuantity = gfgCustomDiscount.state.allowedGetYQuantity[customDiscount?.title] || 0; cartItems.forEach((item) => { item.discounts.forEach((discount) => { const discountMessage = customDiscount?.discountSettings.rulesData?.rulesGlobalList?.[0]?.rulesList?.[0]?.message if(discount.title === customDiscount?.title || discount?.title === discountMessage) { cartItemsWithDiscount += item.quantity; } }) }) return cartItemsWithDiscount >= allowedGetYQuantity; } catch (error) { gfg.utility.debugConsole("Error inside gfgBXGY checkIfGetYProductsCanBeAddedToCart", error); } }, checkIfWidgetShouldBeVisible: async function (customDiscount, pageType) { try { const getYRule = customDiscount?.discountSettings?.rulesData?.rulesGlobalList?.[0]?.rulesList?.[0]?.getYRule if(!customDiscount) { return false; } //this means BUYX condition for BUYX_GETY_DISCOUNT is not fulfilled const isBuyXConditionFulfilled = gfgCustomDiscount.state.BuyXConditionFulfilled[customDiscount?.title] const alwaysKeepVisible = customDiscount?.widgetSettings?.displaySettings?.alwaysKeepVisible; if( !alwaysKeepVisible && !isBuyXConditionFulfilled && customDiscount?.discountFunctionType === "BUYX_GETY_DISCOUNT" ) { return false; } if(!customDiscount?.widgetSettings) { return false; } const isExcludeCondition = getYRule?.ruleValue?.operatorType === "exclude"; if(isExcludeCondition) { return false; } const compatibleRuleTypes = ["products", "collections", "productVariants", "productTags", "productTypes"] if(!compatibleRuleTypes.includes(getYRule.ruleType)) { return false; } const isActiveCampaign = gfg.customDiscountValidationFunctions.checkForActiveCampaign(customDiscount) if (!isActiveCampaign) { return false; } const getYProducts = gfgCustomDiscount?.state?.productsDataForBuyXGetY[customDiscount?.title]; if(getYProducts.length == 0) { return false; } const isRelevantToCurrentPage = await gfg.customDiscountValidationFunctions.checkCustomDiscountWidgetIsRelevantToCurrentPage(customDiscount, pageType); if (!isRelevantToCurrentPage) { return false; } return true; } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount gfgBXGY checkIfWidgetShouldBeVisible fn", error); } }, prepareVariantDetailsBody: function (productData, variantId, discountSettings) { try { const variantData = productData?.variants?.find((variant) => variant.id === variantId) const {variantPrice, variantDiscountedPrice } = gfgCustomDiscount.gfgBXGY.f.getVariantPrices(productData, discountSettings, variantId) const productImage = gfgCustomDiscount.utility.createElementWithAttributes("img", { class : "gfgBuyXGetYVariantSelectorProductImage", src: variantData?.image?.originalSrc }); const productTitle = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGetYProductTitle", innerText: productData?.title }); const productDiscountedPrice = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGetYProductDiscountedPrice", innerText: variantDiscountedPrice }); const productOriginalPrice = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGetYProductOriginalPrice", innerText: variantPrice }); const mainBody = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGeyYProductDetailsBody gfgRowFlexContainer" }); const productDetailsContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGetYProductDetailsContainer gfgColFlexContainer" }); const productPriceContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgRowFlexContainer" }); productPriceContainer.append(productDiscountedPrice, productOriginalPrice) productDetailsContainer.append(productTitle, productPriceContainer) mainBody.append(productImage, productDetailsContainer) const bodyWrapper = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGeyYProductDetailsBodyWrapper" }); bodyWrapper.append(mainBody); return bodyWrapper; } catch (error) { gfg.utility.debugConsole("error inside gfgBXGY prepareVariantDetailsBody", error); } }, prepareVariantSelector: function (productData, variantId, discountSettings, widgetSettings, productIndex, isSingleProductCard, isBtnDisabled) { try { let currLocale = gfg.gfgUnifiedWidget.f.checkMultipleLanguagePresent(widgetSettings?.widgetBodyTextContent) ? gfg.utility.getLocale() : "en"; if(!widgetSettings?.widgetBodyTextContent?.[currLocale]?.addToCartButtonText) { currLocale = "en"; } const addToCartBtnText = widgetSettings?.widgetBodyTextContent?.[currLocale]?.addToCartButtonText || "Add to Cart"; const gfgParentContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : `gfgBuyXGetYVariantSelector gfgColFlexContainer ${isSingleProductCard ? "gfgBuyXGetYSingleProductCard" : ""}`, "data-productindex" : productIndex }); const backButton = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGetYVariantSelectorBackBtn", innerHTML: CONSTANT_ARROW_SVG_APP7EXT }); const gfgMainBody = gfgCustomDiscount.gfgBXGY.f.prepareVariantDetailsBody(productData, variantId, discountSettings); const gfgMainBodyContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class : "gfgBuyXGeyYProductDetailsContainer" }); gfgMainBodyContainer.append(gfgMainBody); const gfgSelectInputContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYVariantSelectorSelectInputContainer" }) if(productData?.variants?.length > 1) { //if there is only one variant, then there is no need to prepare the select input options const gfgSelectInputLabel = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYVariantSelectorSelectInputLabel", innerText: "Variants" }) const gfgSelectInputWrapper = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYVariantSelectorSelectInputWrapper" }); const gfgSelectInput = gfgCustomDiscount.utility.createElementWithAttributes("select", { class: "gfgBuyXGetYVariantSelectorSelectInput", style: `background:${widgetSettings?.styleSettings?.widgetDropdownColor || "#ffffff"} ;`, }); const gfgSelectInputIcon = gfgCustomDiscount.utility.createElementWithAttributes("img", { class: "gfgBuyXGetYVariantSelectorSelectInputIcon", src: "https://d1cjetlwgplgi5.cloudfront.net/public/CaretDown.svg" }); for(let variant of productData?.variants) { const option = gfgCustomDiscount.utility.createElementWithAttributes("option", { class : "variantSelectorOption", innerText: variant?.title, value: variant?.id}); gfgSelectInput.append(option) } gfgSelectInputWrapper.append(gfgSelectInput, gfgSelectInputIcon) gfgSelectInputContainer.append(gfgSelectInputLabel, gfgSelectInputWrapper); } const gfgAddToCartButton = gfgCustomDiscount.utility.createElementWithAttributes("button", { class: `gfgBuyXGetYProductAddToCartBtn gfgBuyXGetYProductBtn gfgBuyXGetYVariantScreenAddToCartBtn ${isBtnDisabled && "disabled"}`, innerText: addToCartBtnText, style: `background:${widgetSettings?.styleSettings?.widgetButtonColor || "#000000"}; color:${widgetSettings?.styleSettings?.widgetBackgroundColor || "#ffffff"};`, 'data-productindex': productIndex }); gfgParentContainer.append((isSingleProductCard ? "" : backButton), gfgMainBody, gfgSelectInputContainer, gfgAddToCartButton) return gfgParentContainer; } catch (error) { gfg.utility.debugConsole("error in gfgBXGY prepareVariantSelector", error) } }, prepareProductCard: function (product, customDiscount, productIndex) { try { const { widgetSettings, discountSettings } = customDiscount; const neededDiscountDetails = { discountType: discountSettings?.type, discountValue: discountSettings?.rulesData?.rulesGlobalList?.[0]?.rulesList?.[0]?.discountValue, } const { variantPrice: productPrice, variantDiscountedPrice: productDiscountedPrice } = gfgCustomDiscount?.gfgBXGY?.f?.getVariantPrices(product, neededDiscountDetails, product?.variants?.[0]?.id); let currLocale = gfg.gfgUnifiedWidget.f.checkMultipleLanguagePresent(widgetSettings?.widgetBodyTextContent) ? gfg.utility.getLocale() : "en"; if(!widgetSettings?.widgetBodyTextContent?.[currLocale]?.selectButtonText) { currLocale = "en"; } const selectBtnText = widgetSettings?.widgetBodyTextContent?.[currLocale]?.selectButtonText || "Select"; const addToCartBtnText = widgetSettings?.widgetBodyTextContent?.[currLocale]?.addToCartButtonText || "Add to Cart"; const productImage = gfgCustomDiscount.utility.createElementWithAttributes("img", { class: "gfgBuyXGetYProductImage", src: product?.featuredImage?.originalSrc }); const productTitle = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYProductTitle", innerText: product.title }); const productPriceContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgRowFlexContainer gfgProductCardPriceContainer" }); const productPriceEle = gfgCustomDiscount.utility.createElementWithAttributes("div", { class:"gfgBuyXGetYProductDiscountedPrice", innerText: productDiscountedPrice }) const productTitleAndPriceContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgColFlexContainer gfgTitleAndPriceContainer"}) const productSelectButton = gfgCustomDiscount.utility.createElementWithAttributes("button", { class: `gfgBuyXGetYProductBtn ${product.variants.length > 1 ? "gfgBuyXGetYProductSelectBtn" : "gfgBuyXGetYProductAddToCartBtn"} ${customDiscount.isBuyXGetYWidgetDisabled && "disabled"}`, innerText: product.variants.length > 1 ? selectBtnText : addToCartBtnText, "data-productindex" : productIndex, style: `background:${widgetSettings?.styleSettings?.widgetButtonColor || "#000000"}; color:${widgetSettings?.styleSettings?.widgetBackgroundColor || "#ffffff"};` }); // if(customDiscount.isBuyXGetYWidgetDisabled) { // productSelectButton.disabled = true; // } const productOriginalPrice = gfgCustomDiscount.utility.createElementWithAttributes("div", { class:"gfgBuyXGetYProductOriginalPrice", innerText: productPrice }) productPriceContainer.append(productPriceEle, productOriginalPrice) productTitleAndPriceContainer.append(productTitle, productPriceContainer) const productCardWrapper = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYProductCardWrapper" }); productCardWrapper.append(productImage, productTitleAndPriceContainer, productSelectButton) return productCardWrapper; } catch (error) { gfg.utility.debugConsole("error in gfgBXGY prepareProductCard", error) } }, prepareProductsList: function (customDiscount) { try { const productsList = gfgCustomDiscount?.state?.productsDataForBuyXGetY[customDiscount?.title]; const { discountSettings } = customDiscount; if (productsList.length == 1) { const neededDiscountSettings = { discountType: discountSettings?.type, discountValue: discountSettings?.rulesData?.rulesGlobalList?.[0]?.rulesList?.[0]?.discountValue, discountFunctionType: customDiscount?.discountFunctionType, discountTitle: customDiscount?.title, } const singleProductCard = gfgCustomDiscount.gfgBXGY.f.prepareVariantSelector(productsList[0], productsList[0]?.variants?.[0]?.id, neededDiscountSettings, customDiscount?.widgetSettings, 0, true, customDiscount?.isBuyXGetYWidgetDisabled); return singleProductCard; } else { const productListWrapper = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: `gfgBuyXGetYProductListWrapper${productsList.length == 2 ? " gfgTwoColumnGrid" : ""}${productsList.length == 3 ? " gfgThreeColumnGrid" : ""}`}); productsList.forEach((product, index) => { const productCard = gfgCustomDiscount.gfgBXGY.f.prepareProductCard(product, customDiscount, index); if(productCard) { productListWrapper.append(productCard); } }) return productListWrapper; } } catch (error) { gfg.utility.debugConsole("error in gfgBXGY prepareProductsList", error) } }, prepareAccordionAction: function (customDiscount) { try { const { widgetSettings, discountSettings } = customDiscount; let currLocale = gfg.gfgUnifiedWidget.f.checkMultipleLanguagePresent(widgetSettings?.titleBar) ? gfg.utility.getLocale() : "en"; if(!widgetSettings?.titleBar?.[currLocale]?.conditionMetHeadingText) { currLocale = "en"; } const parentContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYCustomDiscountAccordionAction gfgRowFlexContainer", style: `background:${widgetSettings?.styleSettings?.widgetBackgroundColor || "transparent"}; outline: 0.8px solid ${widgetSettings?.styleSettings?.widgetBorderColor || "#3c3c3c"}; color: ${widgetSettings?.styleSettings?.widgetTextColor || "#3c3c3c"} `, }); const widgetTitleContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgRowFlexContainer gfgBuyXGetYAccordionTitleContainer" }); const arrowIconContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgAccordionChild gfgAccordionControl" }); const widgetTitleIcon = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYAccordionIcon", innerHTML: BXGY_ICONS[widgetSettings?.titleBar?.conditionMetIconUrl]}); const widgetTitleText = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgAccordionChild gfgBuyXGetYAccordionTitle", innerText: widgetSettings?.titleBar?.[currLocale]?.conditionMetHeadingText }); const svgIcon = widgetTitleIcon.firstChild; svgIcon.setAttribute("fill", widgetSettings?.styleSettings?.titleBarIconColor) widgetTitleContainer.append(widgetTitleIcon, widgetTitleText); arrowIconContainer.innerHTML = CONSTANT_ARROW_SVG_APP7EXT; parentContainer.append(widgetTitleContainer, arrowIconContainer); return parentContainer; } catch (error) { gfg.utility.debugConsole("error in gfgBXGY prepareAccordionAction", error) } }, prepareAccordionBody: function (customDiscount) { try { const { widgetSettings, discountSettings } = customDiscount; const productsList = gfgCustomDiscount?.state?.productsDataForBuyXGetY[customDiscount?.title]; const isScrollArrowsNeeded = productsList?.length > 3; const productsListEle = gfgCustomDiscount.gfgBXGY.f.prepareProductsList(customDiscount); const variantSelectorContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYVariantSelectorContainer" }); const accordionBodyContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYCustomDiscountAccordionBodyContainer", style: `background:${widgetSettings?.styleSettings?.widgetBackgroundColor || "transparent"}; color: ${widgetSettings?.styleSettings?.widgetTextColor || "#3c3c3c"} `, }); const accordionBody = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYCustomDiscountAccordionBody" }); const scrollButtonLeft = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYCustomDiscountScrollButton left", innerHTML: CONSTANT_ARROW_SVG_APP7EXT }); const scrollButtonRight = gfgCustomDiscount.utility.createElementWithAttributes("div", { class: "gfgBuyXGetYCustomDiscountScrollButton right", innerHTML: CONSTANT_ARROW_SVG_APP7EXT }); accordionBody.append(productsListEle, variantSelectorContainer, (isScrollArrowsNeeded ? scrollButtonLeft : ""), (isScrollArrowsNeeded ? scrollButtonRight : "")); accordionBodyContainer.append(accordionBody); return accordionBodyContainer; } catch (error) { gfg.utility.debugConsole("error in gfgBXGY prepareAccordionBody", error) } }, prepareUI: function (customDiscount, pageType) { try { const { widgetSettings, discountSettings } = customDiscount; // uncomment this later // if(!widgetSettings) { // return; // } const neededDiscountSettings = { discountType: discountSettings?.type, discountValue: discountSettings?.rulesData?.rulesGlobalList?.[0]?.rulesList?.[0]?.discountValue, discountFunctionType: customDiscount?.discountFunctionType, discountTitle: customDiscount?.title, } const gfgBuyXGetYCustomDiscountParentContainer = gfgCustomDiscount.utility.createElementWithAttributes("div", { id: `${customDiscount?.title}_${pageType}`, class: `gfgBuyXGetYCustomDiscountParentContainer_${pageType} gfgBuyXGetYCustomDiscountParentContainer`, "data-discountsettings": JSON.stringify(neededDiscountSettings), "data-widgetsettings": JSON.stringify(widgetSettings), }); const gfgBuyXGetYCustomDiscountAccordionAction = gfgCustomDiscount.gfgBXGY.f.prepareAccordionAction(customDiscount); const gfgBuyXGetYCustomDiscountAccordionBodyContainer = gfgCustomDiscount.gfgBXGY.f.prepareAccordionBody(customDiscount); gfgBuyXGetYCustomDiscountParentContainer.append(gfgBuyXGetYCustomDiscountAccordionAction); gfgBuyXGetYCustomDiscountParentContainer.append(gfgBuyXGetYCustomDiscountAccordionBodyContainer); return gfgBuyXGetYCustomDiscountParentContainer; } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount gfgBXGY prepareUI fn", error); } }, }, actions: { registerActions: function (pageType) { try { const customDiscountsWidgets = document.querySelectorAll(`.gfgBuyXGetYCustomDiscountParentContainer_${pageType}`); customDiscountsWidgets.forEach((customDiscountBuyXGetYAccordionRef) => { if(!customDiscountBuyXGetYAccordionRef) { throw new Error("customDiscountBuyXGetYAccordionRef was not found") } customDiscountBuyXGetYAccordionRef.addEventListener("click", async (e) => { if(e.target.closest(".gfgBuyXGetYCustomDiscountAccordionAction")) { gfgCustomDiscount.gfgBXGY.actions.toggleAccordion(e, pageType) return; } if(e.target.classList.contains("gfgBuyXGetYProductSelectBtn")){ e.preventDefault(); e.stopPropagation(); if(e.target.classList.contains("disabled")) { return; } gfgCustomDiscount.gfgBXGY.actions.openVariantSelector(e, pageType) return; } if(e.target.classList.contains("gfgBuyXGetYProductAddToCartBtn")) { e.preventDefault(); e.stopPropagation(); if(e.target.classList.contains("disabled")) { return; } let selectedVariantId; const selectInputContainer = e.target.previousElementSibling; const selectInput = selectInputContainer.querySelector(".gfgBuyXGetYVariantSelectorSelectInput") if(e.target.classList.contains("gfgBuyXGetYVariantScreenAddToCartBtn") && selectInput) { selectedVariantId = selectInput.value; } else { const productIndex = Number(e?.target?.dataset?.productindex); const productData = gfgCustomDiscount.gfgBXGY.f.getProductDataFromIndex(e, productIndex, pageType); selectedVariantId = productData?.variants?.[0]?.id } selectedVariantId = selectedVariantId.split("/")[4]; const discountSettings = JSON.parse(e?.target?.closest?.(`.gfgBuyXGetYCustomDiscountParentContainer_${pageType}`)?.dataset?.discountsettings || ""); // e.target.innerText = "Adding" const btnText = e.target.innerHTML; const textColor = e.target.style.color || "#3c3c3c"; e.target.disabled = true; e.target.innerHTML = `
`; await gfgCustomDiscount.gfgBXGY.f.addGetYEligibleItemToCart(selectedVariantId, discountSettings) // e.target.innerText = "Add to Cart" e.target.disabled = false; e.target.innerHTML = btnText; return; } if(e.target.closest(".gfgBuyXGetYVariantSelectorBackBtn")) { e.preventDefault(); e.stopPropagation(); gfgCustomDiscount.gfgBXGY.actions.handleBackForVariantSelector(e, pageType); return; } if(e.target.closest(".gfgBuyXGetYCustomDiscountScrollButton")) { e.preventDefault(); e.stopPropagation(); gfgCustomDiscount.gfgBXGY.actions.handleScrollButtonClick(e, pageType); return; } }) gfgCustomDiscount.gfgBXGY.actions.registerChangeEventForVariantSelectorInput(pageType) }) } catch (error) { gfg.utility.debugConsole("error in customDiscountLogic gfgBXGY registerActions", error) } }, registerChangeEventForVariantSelectorInput : function (pageType) { try { const variantSelectorInput = document.querySelector(".gfgBuyXGetYVariantSelectorSelectInput"); variantSelectorInput.addEventListener("change", (e) => { gfgCustomDiscount.gfgBXGY.actions.handleVariantChangeForVariantSelector(e, pageType); }) } catch (error) { gfg.utility.debugConsole("error in customDiscountLogic gfgBXGY registerChangeEventForVariantSelectorInput", error) } }, toggleAccordion: function (event) { try { const accordionAction = event.target.closest(".gfgBuyXGetYCustomDiscountAccordionAction") const accordionControl = accordionAction.querySelector(".gfgAccordionControl") const accordionBody = accordionAction.nextElementSibling; accordionControl.classList.toggle("accordion-state-open") accordionBody.classList.toggle("accordion-state-open") } catch (error) { gfg.utility.debugConsole("error in gfgBXGY toggleAccordion", error) } }, openVariantSelector: function (event, pageType) { try { const accordionBody = event.target.closest(".gfgBuyXGetYCustomDiscountAccordionBody"); const productIndex = Number(event?.target?.dataset?.productindex); const productData = gfgCustomDiscount.gfgBXGY.f.getProductDataFromIndex(event, productIndex, pageType); const discountSettings = JSON.parse(event?.target?.closest?.(`.gfgBuyXGetYCustomDiscountParentContainer_${pageType}`)?.dataset?.discountsettings || ""); const widgetSettings = JSON.parse(event?.target?.closest?.(`.gfgBuyXGetYCustomDiscountParentContainer_${pageType}`)?.dataset?.widgetsettings || ""); const variantSelectorEle = gfgCustomDiscount.gfgBXGY.f.prepareVariantSelector(productData, productData?.variants?.[0]?.id, discountSettings, widgetSettings, productIndex) const variantSelectorContainer = accordionBody.querySelector(".gfgBuyXGetYVariantSelectorContainer") variantSelectorContainer.innerHTML = "" variantSelectorContainer.append(variantSelectorEle) gfgCustomDiscount.gfgBXGY.actions.registerChangeEventForVariantSelectorInput(pageType); accordionBody.classList.add("variant-selector-open"); } catch (error) { gfg.utility.debugConsole("error in gfgBXGY openVariantSelector", error) } }, handleBackForVariantSelector: function (event) { try { const accordionBody = event.target.closest(".gfgBuyXGetYCustomDiscountAccordionBody"); accordionBody.classList.remove("variant-selector-open"); const variantSelectorContainer = accordionBody.querySelector(".gfgBuyXGetYVariantSelectorContainer") variantSelectorContainer.innerHTML = "" } catch (error) { gfg.utility.debugConsole("error in gfgBXGY handleBackForVariantSelector", error) } }, handleVariantChangeForVariantSelector: function (event, pageType) { try { const selectedValue = event.target.value; const discountSettings = JSON.parse(event?.target?.closest?.(`.gfgBuyXGetYCustomDiscountParentContainer_${pageType}`)?.dataset?.discountsettings || ""); const productIndex = Number(event?.target?.closest?.(".gfgBuyXGetYVariantSelector")?.dataset?.productindex); const productData = gfgCustomDiscount.gfgBXGY.f.getProductDataFromIndex(event, productIndex, pageType); const selectedVariantDetails = gfgCustomDiscount.gfgBXGY.f.prepareVariantDetailsBody(productData, selectedValue, discountSettings); const variantDetailsBodyWrapper = document.querySelector(".gfgBuyXGeyYProductDetailsBodyWrapper"); variantDetailsBodyWrapper.innerHTML = ""; variantDetailsBodyWrapper.append(selectedVariantDetails) } catch (error) { gfg.utility.debugConsole("error in gfgBXGY handleVariantChangeForVariantSelector", error) } }, handleScrollButtonClick: function (event, pageType) { try { const accordionBody = event.target.closest(".gfgBuyXGetYCustomDiscountAccordionBody"); const parentContainer = accordionBody.closest(`.gfgBuyXGetYCustomDiscountParentContainer_${pageType}`); const productList = parentContainer.querySelector('.gfgBuyXGetYProductListWrapper'); const scrollButton = event.target.closest(".gfgBuyXGetYCustomDiscountScrollButton"); const scrollAmount = 200; window.productList = productList; if (scrollButton.classList.contains('left')) { productList.scrollLeft -= scrollAmount; } else { productList.scrollLeft += scrollAmount; } } catch (error) { gfg.utility.debugConsole("error in gfgBXGY handleScrollButtonClick", error) } } } }, }; let CDCount = 0; let isCDInitFnExecuted = false; const maxCDAttempts = 400; const intervalTimeCD = 200; const intervalIdCD = setInterval(() => { try { if (!window.gfg) { CDCount++; } if (CDCount >= maxCDAttempts) { gfg.utility.debugConsole("Something went wrong in loading gfg script..."); clearInterval(intervalIdCD); return; } const isSettingsDataPresentAndGfgPresent = window.gfg && gfg.settings && Object.keys(gfg.settings).length > 0; if (!isCDInitFnExecuted && isSettingsDataPresentAndGfgPresent) { gfg.utility.debugConsole("Initializing gfgCustomDiscount..."); isCDInitFnExecuted = true; gfgCustomDiscount.init(); } } catch (error) { gfg.utility.debugConsole("Error inside gfgCustomDiscount interval fn", error); } }, intervalTimeCD);