Shipping Calendar Integration on Checkout Page (SDK)

Fenix Global Calendar Script in the Shopify themes

How to configure Fenix global Calendar scripts in the Shopify theme? 

 

  • Rename the duplicated theme as Fenix Integration-yourthemename for better visibility and avoid confusion for other developers. Refer to the below sample screenshot

  • Edit the theme code. Refer to the below sample screenshot

  • Search for the theme file. Refer to the below sample screenshot

  • Search for checkout.liquid keyword in the search box.Refer to the below sample screenshot

 

  • Shopify Code Editor.Refer to the below sample screenshot

 

 

  • The below code integration should be available in the theme for any further changes or while shifting to a completely brand new theme. 

  • Insert below Code Snippet in checkout.liquid file before or inside the </head> tag .

<link rel="stylesheet" href="https://sp1.cdn.fenixcommerce.com/v1/fenix-calendar.min.css"> <script type="text/javascript" src="https://sp1.cdn.fenixcommerce.com/v1/fenix-calendar.min.js"></script>

 

Fenix Checkout code snippet Features.

  • It coverts the whole shipping method section in a detailed calendar view.

  • The calendar view represents all the shipping options within a particular date, along with pricing.

Fenix Estimated Delivery Integration on Checkout page 

  • Please use the above theme.

  • Search the Snippets keyword in the search and click on Add a new Snippet. Refer to the below sample screenshot.

  • Create two new snippets files named fenix-calendar.liquid and fenix-checkout-attributes.liquid.

  • Add below snippets into newly added fenix-calendar.liquid file

<style> #fenix-checkout-calendar .fc-view-harness.fc-view-harness-active { height: 465px !important; } #fenix-checkout-calendar .fc-daygrid-body.fc-daygrid-body-unbalanced { width: 100% !important; } #fenix-checkout-calendar .fc-col-header { width: 100% !important; } .fenix-separator-month { width: 100px; text-align: left; margin: 8px 15px; } .fc .fc-scroller.fc-scroller-liquid-absolute { overflow-y: auto !important; } .fc .fc-daygrid-day-frame { position: relative; min-height: 77px; cursor: pointer; } @media(max-width: 767px) { .section.section--shipping-method .section__header { padding: 25px 0; } .fc .fc-daygrid-day-frame { display: flex; flex-direction: column; justify-content: center; } } </style> <script> const fastplane = `<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="32.000000pt" height="32.000000pt" viewBox="0 0 32.000000 32.000000" preserveAspectRatio="xMidYMid meet"> <g transform="translate(0.000000,32.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none"> <path d="M236 281 l-38 -39 -79 26 c-73 24 -80 25 -95 10 -18 -18 -24 -12 75 -73 l43 -27 -33 -34 c-22 -22 -41 -34 -55 -32 -12 2 -29 -4 -39 -12 -16 -13 -15 -15 16 -25 24 -8 36 -20 44 -44 10 -31 12 -32 25 -16 8 10 14 27 12 39 -2 14 10 33 32 55 l34 33 15 -23 c8 -13 27 -44 42 -68 25 -40 28 -42 43 -27 15 15 14 22 -10 95 l-25 79 41 41 c34 34 39 44 30 59 -18 28 -37 25 -78 -17z"/> </g> </svg>`; const freightplane = `<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="32.000000pt" height="32.000000pt" viewBox="0 0 512.000000 512.000000" preserveAspectRatio="xMidYMid meet"> <g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none"> <path d="M3909 4136 c-123 -50 -261 -160 -541 -427 -107 -103 -184 -169 -196 -169 -11 0 -89 17 -173 39 -85 21 -166 41 -181 45 -66 15 -75 64 -23 126 19 23 35 52 35 64 0 25 -108 136 -133 136 -8 0 -61 -51 -117 -112 -57 -62 -113 -117 -124 -122 -15 -6 -91 10 -261 53 -258 67 -267 67 -301 16 -12 -19 -14 -28 -5 -39 6 -7 212 -125 457 -262 253 -141 453 -259 461 -272 12 -17 13 -28 5 -45 -5 -12 -79 -101 -162 -197 -84 -96 -218 -257 -299 -358 -84 -106 -155 -185 -167 -188 -13 -3 -100 20 -206 55 -101 34 -195 61 -209 61 -29 0 -59 -28 -59 -54 0 -11 52 -51 148 -113 219 -142 214 -135 156 -239 -19 -35 -33 -68 -29 -74 4 -7 30 2 70 25 35 19 74 35 87 35 32 0 48 -19 160 -191 53 -81 101 -150 107 -154 15 -9 58 19 65 43 4 11 -21 102 -59 213 -36 107 -64 205 -63 219 2 17 29 45 88 93 197 159 397 325 520 432 72 63 139 115 149 115 10 0 28 -8 39 -17 11 -10 131 -216 267 -458 136 -242 252 -446 257 -452 25 -31 88 10 88 56 0 13 -28 132 -62 264 l-62 240 122 116 c67 64 122 122 122 129 0 23 -111 132 -134 132 -13 0 -42 -21 -71 -50 -90 -91 -110 -72 -164 160 -23 96 -41 188 -41 204 0 24 27 57 153 185 298 304 444 510 463 653 6 43 4 49 -24 77 -38 38 -75 40 -153 7z"/> <path d="M1447 3062 c-229 -230 -417 -424 -417 -433 0 -28 30 -59 58 -59 22 0 100 73 440 413 434 434 439 440 394 480 -10 10 -27 17 -38 17 -12 0 -174 -155 -437 -418z"/> <path d="M1366 2011 c-219 -220 -247 -252 -241 -272 9 -30 32 -49 59 -49 29 0 516 487 516 516 0 12 -8 28 -18 37 -42 39 -56 28 -316 -232z"/> <path d="M2982 1637 c-295 -296 -412 -419 -412 -435 0 -28 34 -62 62 -62 16 0 138 115 435 413 434 433 439 439 397 481 -42 42 -48 37 -482 -397z"/> <path d="M1557 1702 c-203 -203 -307 -314 -307 -328 0 -27 19 -50 49 -59 21 -6 59 29 337 306 214 214 314 320 314 335 0 11 -8 28 -18 37 -43 39 -53 31 -375 -291z"/> <path d="M1758 1403 c-196 -197 -357 -365 -359 -373 -4 -40 19 -70 54 -70 30 0 747 716 747 746 0 27 -30 54 -61 54 -18 0 -108 -84 -381 -357z"/> </g> </svg> `; const truckdel = `<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="32.000000pt" height="32.000000pt" viewBox="0 0 128.000000 128.000000" preserveAspectRatio="xMidYMid meet"> <g transform="translate(0.000000,128.000000) scale(0.100000,-0.100000)" fill="#000000" stroke="none"> <path d="M120 1060 c-8 -15 -6 -24 10 -40 19 -19 32 -20 340 -20 l320 0 0 -148 c0 -102 4 -152 12 -160 8 -8 61 -12 174 -12 l163 0 30 -37 c30 -37 31 -40 31 -140 l0 -103 -37 0 c-34 0 -41 5 -60 39 -30 52 -98 86 -155 78 -49 -7 -85 -31 -117 -80 l-24 -37 -116 0 -116 1 -25 39 c-26 43 -88 80 -133 80 -56 0 -128 -48 -148 -97 -7 -20 -16 -23 -64 -23 -66 0 -103 -26 -85 -60 9 -17 21 -20 73 -20 61 0 62 -1 82 -37 32 -59 66 -78 140 -78 74 0 108 19 140 78 l20 37 117 0 117 0 17 -35 c9 -20 30 -45 47 -57 40 -29 123 -36 165 -14 32 17 82 74 82 94 0 8 23 12 68 12 40 0 73 5 80 12 8 8 12 57 12 153 l0 142 -42 59 c-24 32 -72 115 -108 184 l-65 125 -97 3 c-91 3 -98 4 -98 23 0 11 -5 29 -10 40 -10 18 -25 19 -370 19 -348 0 -360 -1 -370 -20z m933 -215 c20 -42 37 -78 37 -80 0 -3 -49 -5 -110 -5 l-110 0 0 80 0 80 73 0 73 0 37 -75z m-581 -433 c46 -47 11 -132 -56 -132 -59 0 -95 66 -65 121 21 41 86 46 121 11z m546 5 c37 -30 31 -106 -8 -127 -53 -29 -120 8 -120 65 0 69 76 105 128 62z"/> <path d="M80 906 c-6 -8 -10 -25 -8 -38 l3 -23 194 -3 c201 -3 221 1 221 39 0 33 -34 39 -218 39 -144 0 -182 -3 -192 -14z"/> <path d="M12 748 c-15 -15 -15 -41 0 -56 13 -13 361 -18 392 -6 19 7 21 45 4 62 -17 17 -379 17 -396 0z"/> <path d="M82 588 c-6 -6 -12 -20 -12 -29 0 -33 25 -40 135 -37 90 3 110 6 119 21 8 12 8 22 0 35 -9 14 -29 17 -120 20 -77 2 -113 -1 -122 -10z"/> </g> </svg> `; let holidayslist = []; let holidayslistdesc = []; let alldates = []; let allrates = []; let allinputs = []; let saturdayshipping = null; let saturdayshippingtext = null; let datepairrates = []; let aAlldates = []; let aAllrates = []; let aAllinputs = []; let aDatepairrates = []; // create cookie function function createCookie(name, value, days) { var date = new Date(); days = days || 14; // 14 days path = '/'; date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); var expires = '; expires=' + date.toGMTString(); var cookieValue = name + '=' + value + expires + '; path=' + path; cookieValue += '; domain='; document.cookie = cookieValue; } // read cookie function function readCookie(name) { var allCookie = '' + document.cookie; var index = allCookie.indexOf(name); if (name === undefined || name === '' || index === -1) return ''; var ind1 = allCookie.indexOf(';', index); if (ind1 == -1) ind1 = allCookie.length; return unescape(allCookie.substring(index + name.length + 1, ind1)); } function returnShippingName() { let shippingname = ""; $.each($(".content-box__row"), function(index, value) { if ($(value).find(".input-radio:checked").val() != undefined && $(value).find(".input-radio:checked").val() !== null) { shippingname = $(value).find(".radio__label__primary").attr("data-shipping-method-label-title"); } }); return shippingname; } function formatDate(date) { var d = new Date(date), month = '' + (d.getMonth() + 1), day = '' + d.getDate(), year = d.getFullYear(); if (month.length < 2) month = '0' + month; if (day.length < 2) day = '0' + day; return [year, month, day].join('-'); } function activateEddRow(dis, radioid) { $(".fc-day").removeClass("active"); $(dis).addClass("active"); if (radioid !== undefined && radioid !== "") { $("#" + radioid).trigger("click"); $(".section__content").find(".content-box__row").hide(); $("#" + radioid).parent().parent().parent().show(); createCookie("fenix_delivery_date_checkout", $(dis).attr("data-date"), 1); createCookie("fenix_delivery_method_checkout", returnShippingName(), 1); $("input[name='checkout[attributes][FenixCommerce Delivery Date]']").val($(dis).attr("data-date")); $("input[name='checkout[attributes][FenixCommerce Shipping Method]']").val(returnShippingName()); } } var holidays = {}; var ratescard = []; function fenixCheckoutCalendar() { var url = "https://delest-v2.preprod.fenixcommerce.com/fenixdelest/api/v1/holidayinfo/getHolidayInfo"; var xhr = new XMLHttpRequest(); xhr.open("POST", url); xhr.setRequestHeader("accept", "application/json"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { holidays = JSON.parse(xhr.responseText) $.each(holidays.holidays, function(ii, holiday) { holidayslist.push(formatDate(holiday.date)); holidayslistdesc.push(holiday.desc); }); $.each($(".content-box__row"), function(index, value) { if ($(value).find(".small-text").text()) { let dateArray = $(value).find(".small-text").text().split(","); dateArray = dateArray[1].replace(")", ""); // get current year var currentyear = new Date(); currentyear = currentyear.getFullYear(); dateArray = new Date(dateArray + " " + currentyear); // dateArray = new Date(dateArray.getTime() + 330 * 60000); alldates.push(dateArray); let rate = $(value).find('.content-box__emphasis').text().trim(); allrates.push(rate); allinputs.push($(value).find('input').attr('id')); // Saturday Shipping if ($(value).find(".radio__label").text().trim().toLowerCase().indexOf("saturday") > -1) { saturdayshipping = rate; saturdayshippingtext = $(value).find('input').attr('id'); } } }); $.each(alldates, function(ii, date) { datepairrates.push({ 'date': date, 'rates': allrates[ii], 'input': allinputs[ii] }); }); if (datepairrates.length == 0) return false; datepairrates.sort(function(a, b) { return new Date(b.date) - new Date(a.date); }); for (var i = 0; i < datepairrates.length - 1; i++) { let first = datepairrates[i].date; let second = datepairrates[i + 1].date; let diff = first.getTime() - second.getTime(); if (diff > 86400000) { for (var ii = 1; ii < (diff / 86400000); ii++) { let day = new Date(datepairrates[i].date.getTime() - 86400000 * ii); if (day.getDay() != 6) { datepairrates.push({ 'date': day, 'rates': datepairrates[i + 1].rates, 'input': datepairrates[i + 1].input }); } } } } datepairrates.sort(function(a, b) { return new Date(b.date) - new Date(a.date); }); var calendarEl = document.getElementById('fenix-checkout-calendar'); var from_date = new Date(); from_date.setDate(from_date.getDate() + 1); var datefirst = datepairrates[datepairrates.length - 1].date; var firstDay = new Date(datefirst.getFullYear(), datefirst.getMonth(), 1); var from_date_new = firstDay.setDate(firstDay.getDate()); var month = datefirst.getMonth() + 1; // January var d = new Date(datefirst.getFullYear(), month + 1, 0); var last_date_new = d.setDate(d.getDate()); var to_date = new Date(); to_date.setDate(to_date.getDate() + 60); let toDate = new Date(datefirst); toDate.setDate(toDate.getDate() + 40); let smallestdate = new Date(datepairrates[datepairrates.length - 1].date); let leadTimeDays = 41 + Math.ceil(Math.abs(smallestdate - new Date()) / (1000 * 60 * 60 * 24)); // calculate difference here for lead time days //let leadtimedays = 41 + difference days // 2nd may = smallest date - todays date. and Days diff is lead time. for (var i = 1; i < leadTimeDays; i++) { var eventdate = new Date(); eventdate.setDate(eventdate.getDate() + i); let day = eventdate.getDay(); let month = ""; if (eventdate.getMonth() + 1 < 10) { month = "0" + (eventdate.getMonth() + 1); } else { month = eventdate.getMonth() + 1; } let dd = ""; if (eventdate.getDate() < 10) { dd = "0" + (eventdate.getDate()); } else { dd = eventdate.getDate(); } let fulldate = eventdate.getFullYear() + "-" + month + "-" + dd; var d2 = new Date(fulldate); let d3 = new Date(fulldate+ " 00:00"); var datefound = true; var price = allrates[0] + "|" + allinputs[0] + "| truckdel"; // if saturday shipping day == 6 if (d3.getDay() == 6 && saturdayshipping !== null) { price = saturdayshipping + "|" + saturdayshippingtext + "| fastplane"; } for (let i = 0; i < datepairrates.length; i++) { var d1 = new Date(datepairrates[i].date); // resolved Saturday if (d1.getTime() >= d3.getTime() && d3.getDay() == 6 && saturdayshipping !== null) { price = saturdayshipping + "|" + saturdayshippingtext + "| fastplane"; break; } if (d1.getTime() == d3.getTime()) { if (i == 0) { price = datepairrates[i].rates + "|" + datepairrates[i].input + "| truckdel"; } else { price = datepairrates[i].rates + "|" + datepairrates[i].input + "| fastplane"; } break; } } if (day !== 0 && $.inArray(fulldate, holidayslist) === -1 && d3.getTime() >= smallestdate.getTime()) { ratescard.push({ title: price, start: fulldate, constraint: 'businessHours', html: true }); } else if (day !== 0 && $.inArray(fulldate, holidayslist) !== -1 && d3.getTime() >= smallestdate.getTime()) { ratescard.push({ title: holidayslistdesc[holidayslist.indexOf(fulldate)] + "|disable", start: fulldate, constraint: 'businessHours', html: true }); } } function dateRangesss(startDate, endDate) { var start = startDate.split('-'); var end = endDate.split('-'); var startYear = parseInt(start[0]); var endYear = parseInt(end[0]); var dates = []; for (var i = startYear; i <= endYear; i++) { var endMonth = i != endYear ? 11 : parseInt(end[1]) - 1; var startMon = i === startYear ? parseInt(start[1]) - 1 : 0; for (var j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j + 1) { var month = j + 1; var displayMonth = month < 10 ? '0' + month : month; dates.push([i, displayMonth, '01'].join('-')); } } return dates; } let getAllMonths = dateRangesss(new Date(datefirst).toISOString().slice(0, 10), new Date(toDate).toISOString().slice(0, 10)); var firstday = 0; var calendar = new FullCalendar.Calendar(calendarEl, { timeZone: 'local', firstDay: '0', headerToolbar: false, validRange: { start: new Date(datefirst), end: toDate }, initialView: 'myView', views: { myView: { type: "dayGridMonth", duration: { weeks: 6 } // for some reason { days: 30 } doesn't work } }, initialDate: new Date(datefirst), navLinks: false, // can click day/week names to navigate views businessHours: [{ daysOfWeek: [1, 2, 3, 4, 5, 6], }], editable: false, selectable: false, events: ratescard, showNonCurrentDates: false, eventDidMount: function(info) { let element = $(info.el); if (getAllMonths.indexOf(info.event.startStr) > -1) { const separatorMonthName = new Date(info.event.start).toLocaleString('default', { month: 'long' }); const separator = `<div class="fenix-separator-month">${separatorMonthName}</div>`; $(info.el).parents('td.fc-daygrid-day').parent('tr').before(separator); } let html = element.text(); const htmlArray = html.split("|"); let newhtml = ""; let planeicon = ""; let planeiconclass = ""; if (htmlArray[2] !== undefined && htmlArray[2].trim() == "freightplane") { planeicon = freightplane; planeiconclass = "freightplane"; } else if (htmlArray[2] !== undefined && htmlArray[2].trim() == "fastplane") { planeicon = fastplane; planeiconclass = "fastplane"; } else if (htmlArray[2] !== undefined && htmlArray[2].trim() == "truckdel") { planeicon = truckdel; planeiconclass = "truckdel"; } if (htmlArray[1] == "disable") { newhtml = `<span class="holiday-desc fenix-icc" title="${htmlArray[0]}" alt="${htmlArray[0]}" inputid="${htmlArray[1]}">${htmlArray[0]}</span>`; } else { newhtml = `<span class="fenix-icc" inputid="${htmlArray[1]}">${htmlArray[0]}</span><br><span class="${planeiconclass}">${planeicon}</span>`; } element.html(newhtml); if (firstday === 0 && htmlArray[1] != "disable") { setTimeout(() => { $("#" + htmlArray[1]).trigger("click"); $(".section__content").find(".content-box__row").hide(); $("#" + htmlArray[1]).parent().parent().parent().show(); $("#" + htmlArray[1]).addClass("active"); element.parent().parent().parent().parent().addClass("active"); }, 1000); firstday++; } if (htmlArray[1] !== undefined && htmlArray[1] !== "" && htmlArray[1] != "disable") { element.parent().parent().parent().parent().attr("onclick", `activateEddRow(this, "${htmlArray[1]}")`); } if (htmlArray[1] == "disable") { element.parent().parent().parent().parent().addClass("disable-dates"); } } }); calendar.render(); } }; var data = '{"holidayPeriod": 40, "storeName": "kb-dev1.myshopify.com","tenantId": "08b7a83428f548fabd2d29804807da2b" }'; xhr.send(data); } window.Checkout.jQuery(document).on('page:load', function() { if (Shopify.Checkout.step === "shipping_method") { const checkEleExists = setInterval(() => { if (jQuery('.section--shipping-method fieldset[data-shipping-methods] .content-box__row').length > 0) { fenixCheckoutCalendar(); clearInterval(checkEleExists); } }, 40); $("#fenix-checkout-calendar").insertAfter("#main-header"); setTimeout(() => { // update date for inputs if (ratescard !== undefined && ratescard.length > 0) { createCookie("fenix_delivery_date_checkout", ratescard[0].start, 1); createCookie("fenix_delivery_method_checkout", returnShippingName(), 1); $("input[name='checkout[attributes][FenixCommerce Delivery Date]']").val(readCookie("fenix_delivery_date_checkout")); $("input[name='checkout[attributes][FenixCommerce Shipping Method]']").val(returnShippingName()); } }, 1000); } }); </script>
  • Add the below snippets into fenix-checkout-attributes.liquid.

    • This code will show attribute notes in the backend while previewing the order.

<script> let pickupValue = readCookie("fenix_delivery_date_checkout") || "test"; let shippingValue = readCookie("fenix_delivery_method_checkout") || "test1"; const store = { order_attributes: [ 'FenixCommerce Delivery Date', 'FenixCommerce Shipping Method' ] } const storeOrderData = { attributes: { "FenixCommerce Delivery Date": pickupValue, "FenixCommerce Shipping Method": shippingValue } }; const { attributes } = storeOrderData; function injectInputs(element, orderAttributes) { orderAttributes.forEach((attribute) => { const $input = document.querySelectorAll(`input[name="checkout[attributes][${attribute}]"]`); if ($input.length < element.length) { const $newInput = document.createElement('input'); $newInput.type = 'hidden'; $newInput.name = `checkout[attributes][${attribute}]`; element.appendChild($newInput); } }); } function setStoreAttributes(data, orderAttributes) { orderAttributes.forEach((attribute) => { const $inputs = document.querySelectorAll(`input[name="checkout[attributes][${attribute}]"]`); $inputs.forEach((input) => { input.value = data[`${attribute}`]; }); }); } function updateAttributes() { const $cartForms = document.querySelectorAll('form[method="post"].edit_checkout'); if ($cartForms.length > 0) { $cartForms.forEach((form) => { injectInputs(form, store.order_attributes); }); } setStoreAttributes(attributes, store.order_attributes); } document.addEventListener('page:change', updateAttributes); document.addEventListener('page:load', updateAttributes); </script>
  • Fenix Checkout Template code snippet

  • Copy and paste the above-provided code snippet completely into checkout.liquid file, and don't forget to click the Save button. Refer to the below sample screenshot.

 

After following the above steps, The preview will be like the below screenshot,

 

Please contact Fenix Customer Support Team fixdsupport@fenixcommerce.ai in case of any issue in the code snippets and for all queries.

 

This completes the Checkout calendar integration.