The repo for Purrform's main BigCommerce store.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #2 from Teser301/disapearing-dates

Disapearing dates

authored by

Rogerio Romao and committed by
GitHub
c43c5288 cb2b5df2

+739 -769
-11
assets/js/custom.js
··· 1 - // OLD LIVE URL 2 - api_url = 'https://api.purrform.co.uk/api/v3'; 3 - // LOCAL URL for when app is running locally 4 - // api_url = 'http://localhost:8000/api/v3'; 5 - // TESTING URL 6 - // api_url = 'https://app.hsdkezaspn.co.uk/api/v3'; 7 - // live URL 8 - // api_url = 'https://pfapp.purrform.co.uk/api/v3'; 9 - 10 - 11 - fd_api_url = 'https://pfapp.purrform.co.uk/api/v3';
+391
assets/js/delivery.js
··· 1 + // This is a custom function to check if an element is ready in the DOM with MutationObserver (no timers) 2 + (function (win) { 3 + 'use strict'; 4 + 5 + var listeners = [], 6 + doc = win.document, 7 + MutationObserver = win.MutationObserver || win.WebKitMutationObserver, 8 + observer; 9 + 10 + function ready(selector, fn) { 11 + // Store the selector and callback to be monitored 12 + listeners.push({ 13 + selector: selector, 14 + fn: fn, 15 + }); 16 + if (!observer) { 17 + // Watch for changes in the document 18 + observer = new MutationObserver(check); 19 + observer.observe(doc.documentElement, { 20 + childList: true, 21 + subtree: true, 22 + }); 23 + } 24 + // Check if the element is currently in the DOM 25 + check(); 26 + } 27 + 28 + function check() { 29 + // Check the DOM for elements matching a stored selector 30 + for ( 31 + var i = 0, len = listeners.length, listener, elements; 32 + i < len; 33 + i++ 34 + ) { 35 + listener = listeners[i]; 36 + // Query for elements matching the specified selector 37 + elements = doc.querySelectorAll(listener.selector); 38 + for (var j = 0, jLen = elements.length, element; j < jLen; j++) { 39 + element = elements[j]; 40 + // Make sure the callback isn't invoked with the 41 + // same element more than once 42 + if (!element.ready) { 43 + element.ready = true; 44 + // Invoke the callback with the element 45 + listener.fn.call(element, element); 46 + } 47 + } 48 + } 49 + } 50 + 51 + // Expose `ready` 52 + win.ready = ready; 53 + })(this); 54 + 55 + // ------------------------------------------------------// 56 + // change the text on the mobile modal 57 + const mobileModalSelector = '.cartDrawer.optimizedCheckout-orderSummary'; 58 + ready(mobileModalSelector, (mobileModalElement) => { 59 + mobileModalElement.querySelector('.cartDrawer-body a').textContent = 60 + 'Show Details & Rewards'; 61 + }); 62 + 63 + // ------------------------------------------------------// 64 + // This handles 'datepicker' in 'shipping' 65 + const shippingSelector = '#checkout-shipping-options'; 66 + ready(shippingSelector, (shippingOptionsElement) => { 67 + createCalendar(shippingOptionsElement); 68 + }); 69 + 70 + // Attach the datepicker to the shipping options 71 + function createCalendar(parent) { 72 + const existingInstructions = document.getElementById('datepicker-hoster'); 73 + if (existingInstructions) { 74 + return; 75 + } 76 + 77 + // Define the HTML code for the elements 78 + const datePickerHTML = ` 79 + <div id="datepicker-hoster" class="datepicker"> 80 + <div class="cart-total-label"> 81 + <strong style="color: #687d6a;">Delivery Date *</strong> 82 + </div> 83 + <div class="voucher-section"> 84 + <input type="text" id="datepicker" name="datepicker" readonly autocomplete="off" aria-autocomplete="none" class="form-input" style="background: white; width: calc(100%); border-radius: 45px; border-color: #687d6a; color: #687d6a; font-size: 16px; text-align: center;" onkeydown="return false;"> 85 + <p id="datepicker_err" style="display: none; color: red; font-size: 16px; font-weight: normal;"></p> 86 + </div> 87 + <p style="font-size: 16px;">*Next Day delivery is usually available on any orders placed before 12 noon, Monday to Friday. Excluding some postcodes in Scotland.</p> 88 + </div> 89 + `; 90 + parent.insertAdjacentHTML('beforeend', datePickerHTML); 91 + getCalendarData(); 92 + } 93 + 94 + // Get data for the calendar 95 + function getCalendarData() { 96 + // Create calendar and attach date picker logic here 97 + let dailyDeliveryLimit = 250; 98 + const dailySlotValues = {}; 99 + const disabledDates = []; 100 + 101 + // get delivery dates from middleware 102 + fetch('https://purrform-apps-027e.onrender.com/deliveryDates') 103 + .then((response) => { 104 + if (!response.ok) { 105 + throw new Error('Network response was not ok'); 106 + } 107 + return response.json(); 108 + }) 109 + .then((data) => { 110 + disabledDates.push(...data.unavailableDates); 111 + for (const [date, slots] of Object.entries(data.dateSlots)) { 112 + dailySlotValues[date] = slots; 113 + } 114 + if (data.perDay) { 115 + dailyDeliveryLimit = data.perDay; 116 + } 117 + attachDatePicker( 118 + dailyDeliveryLimit, 119 + dailySlotValues, 120 + disabledDates 121 + ); 122 + }) 123 + .catch((error) => { 124 + console.log( 125 + 'Error calling delivery dates middleware endpoint:', 126 + error 127 + ); 128 + // Handle error if necessary 129 + }); 130 + } 131 + 132 + // Attach the calendar to the datepicker and enable/disable dates 133 + function attachDatePicker(dailyDeliveryLimit, dailySlotValues, disabledDates) { 134 + function disableDates(day) { 135 + // day gets implicitly passed by jQuery Calendar 136 + const selectedShippingMethod = document.querySelector( 137 + '.form-checklist-item--selected' 138 + ); 139 + 140 + const isSaturdayDelivery = selectedShippingMethod 141 + .querySelector('.shippingOption-desc') 142 + .textContent.toLowerCase() 143 + .includes('saturday'); 144 + 145 + return isSaturdayDelivery ? enableSaturday(day) : enableWeekdays(day); 146 + } 147 + 148 + // Disable everything except Saturdays 149 + function enableSaturday(day) { 150 + const dayBeingCheckedFormatted = $.datepicker.formatDate( 151 + 'yy-mm-dd', 152 + day 153 + ); 154 + 155 + const dayOfTheWeek = day.getDay(); 156 + const saturdayDay = 6; 157 + 158 + if (dayOfTheWeek !== saturdayDay) { 159 + return [false, '', '']; // disabled boolean, css class, tooltip 160 + } 161 + 162 + let slotsForThisDay = 163 + dailySlotValues[dayBeingCheckedFormatted] ?? dailyDeliveryLimit; 164 + if (slotsForThisDay > dailyDeliveryLimit) { 165 + // this should never happen, but just in case 166 + slotsForThisDay = dailyDeliveryLimit; 167 + } 168 + 169 + // Enable check 170 + const isSaturdayEnabled = 171 + slotsForThisDay > 0 && 172 + !disabledDates.includes(dayBeingCheckedFormatted); 173 + 174 + return [isSaturdayEnabled, '', `Slots available: ${slotsForThisDay}`]; // enabled boolean, css class, tooltip 175 + } 176 + 177 + // Enable weekdays 178 + function enableWeekdays(day) { 179 + const dayBeingCheckedFormatted = $.datepicker.formatDate( 180 + 'yy-mm-dd', 181 + day 182 + ); 183 + 184 + const dayOfTheWeek = day.getDay(); 185 + // Disable if 6 (Saturday) 0 (Sunday) or 1 (Monday) 186 + if (dayOfTheWeek === 6 || dayOfTheWeek === 0 || dayOfTheWeek === 1) { 187 + return [false, '', '']; // disabled boolean, css class, tooltip 188 + } 189 + 190 + let slotsForThisDay = 191 + dailySlotValues[dayBeingCheckedFormatted] ?? dailyDeliveryLimit; 192 + if (slotsForThisDay > dailyDeliveryLimit) { 193 + // this should never happen, but just in case 194 + slotsForThisDay = dailyDeliveryLimit; 195 + } 196 + 197 + const isEnabledDay = 198 + slotsForThisDay > 0 && 199 + !disabledDates.includes(dayBeingCheckedFormatted); 200 + 201 + return [isEnabledDay, '', `Slots available: ${slotsForThisDay}`]; // enabled boolean, css class, tooltip 202 + } 203 + 204 + $('#datepicker').datepicker({ 205 + beforeShowDay: disableDates, 206 + onClose: function () { 207 + const datepicker = document.querySelector('#datepicker'); 208 + const deliveryDate = datepicker.value; 209 + if (deliveryDate) { 210 + sessionStorage.setItem('deliveryDate', deliveryDate); 211 + } 212 + }, 213 + defaultDate: '+1D', 214 + minDate: '+1D', 215 + maxDate: '+21D', 216 + dateFormat: 'd MM, yy', 217 + showOtherMonths: true, 218 + firstDay: 1, 219 + placeholder: 'Select Date', 220 + }); 221 + 222 + $('#datepicker').attr('placeholder', 'Select Date'); 223 + } 224 + 225 + // ------------------------------------------------------// 226 + // handle proceed button 227 + const proceedButtonSelector = '#proceedButton'; 228 + ready(proceedButtonSelector, (proceedButton) => { 229 + proceedButton.addEventListener('click', function (e) { 230 + e.preventDefault(); 231 + const deliveryDate = sessionStorage.getItem('deliveryDate'); 232 + const customerMessage = sessionStorage.getItem('customerMessage'); 233 + const realContinueButton = document.querySelector( 234 + '#checkout-shipping-continue' 235 + ); 236 + 237 + const datepickerErrorElement = 238 + document.getElementById('datepicker_err'); 239 + 240 + if (!deliveryDate) { 241 + datepickerErrorElement.textContent = 242 + 'Please select a delivery date before proceeding.'; 243 + datepickerErrorElement.style.display = 'block'; 244 + return; 245 + } 246 + // Proceed with the form submission 247 + datepickerErrorElement.style.display = 'none'; 248 + 249 + const selectElement = document.querySelector('#delivery_inst_tag'); 250 + const selectedValue = selectElement.value; 251 + const deliveryInstructions = 252 + selectedValue === 'user_instruction' && customerMessage.length > 0 253 + ? customerMessage 254 + : 'Will be in'; 255 + 256 + const fullInstructions = `${deliveryDate} | ${deliveryInstructions}`; 257 + 258 + // Update the customer message with the delivery instructions 259 + const checkoutId = jsContext.checkoutId; 260 + fetch(`/api/storefront/checkouts/${checkoutId}`, { 261 + method: 'PUT', 262 + headers: { 263 + 'Content-Type': 'application/json', 264 + }, 265 + body: JSON.stringify({ 266 + customerMessage: fullInstructions, 267 + }), 268 + }) 269 + .then((response) => { 270 + if (!response.ok) { 271 + throw new Error('PUT request failed'); 272 + } 273 + return response.json(); // Parse the JSON response 274 + }) 275 + .then((data) => { 276 + sessionStorage.removeItem('deliveryDate'); 277 + sessionStorage.removeItem('customerMessage'); 278 + realContinueButton.click(); 279 + }) 280 + .catch((error) => { 281 + console.error('Error:', error); 282 + }); 283 + }); 284 + }); 285 + 286 + // ------------------------------------------------------// 287 + // This handles the delivery details / customer message 288 + const deliveryDetailsSelector = 289 + 'fieldset.form-fieldset[data-test="checkout-shipping-comments"]'; 290 + ready(deliveryDetailsSelector, (deliveryDetailsElement) => { 291 + const existingInstructions = document.getElementById('instruction-wrap'); 292 + if (existingInstructions) { 293 + return; 294 + } 295 + 296 + // generate custom HTML 297 + const instructionsDiv = ` 298 + <div id="instruction-wrap" class="delivery_instruction_wrapper"> 299 + <div class="del_cont"> 300 + <label class="form-legend optimizedCheckout-headingSecondary">Delivery Details</label> 301 + <select class="form-select form-select--small" id="delivery_inst_tag"> 302 + <option value="Will be in">Will be in</option> 303 + <option value="user_instruction">User instruction</option> 304 + </select> 305 + <textarea style="display:none" name="delivery_user_text" id="delivery_user_text" rows="3" cols="40" class="form-input"></textarea> 306 + </div> 307 + <div id="proceedButton">Continue</div> 308 + </div> 309 + `; 310 + 311 + deliveryDetailsElement.insertAdjacentHTML('afterend', instructionsDiv); 312 + 313 + const selectElement = document.getElementById('delivery_inst_tag'); 314 + const textarea = document.getElementById('delivery_user_text'); 315 + 316 + selectElement.addEventListener('change', function () { 317 + const selectedValue = selectElement.value; 318 + if (selectedValue === 'user_instruction') { 319 + textarea.style.display = 'block'; 320 + textarea.style.margin = '10px 0'; 321 + textarea.focus(); 322 + } else { 323 + textarea.style.display = 'none'; 324 + } 325 + }); 326 + 327 + textarea.addEventListener('input', function () { 328 + const deliveryInstructions = textarea.value; 329 + sessionStorage.setItem('customerMessage', deliveryInstructions); 330 + }); 331 + }); 332 + 333 + // ------------------------------------------------------// 334 + // This handles the payment continue button 335 + const paymentContinueButtonSelector = '#checkout-payment-continue'; 336 + ready(paymentContinueButtonSelector, (paymentContinueButton) => { 337 + const fakeButton = document.createElement('div'); 338 + const missingWarn = document.createElement('p'); 339 + const parent = document.querySelector( 340 + '.checkout-step--payment .checkout-form .form-actions' 341 + ); 342 + 343 + // hide the real button 344 + paymentContinueButton.style.display = 'none'; 345 + 346 + // Generate placeholder button 347 + fakeButton.innerHTML = 'Place Order'; 348 + fakeButton.id = 'fakeButton'; 349 + fakeButton.style.display = 'block'; 350 + 351 + // Generate warning text 352 + missingWarn.classList.add('warning'); 353 + missingWarn.textContent = 354 + "Cannot find delivery date. Please review your 'shipping' details."; 355 + missingWarn.style.display = 'none'; 356 + 357 + // Append the button to the body of the HTML document 358 + parent.appendChild(missingWarn); 359 + parent.appendChild(fakeButton); 360 + 361 + fakeButton.addEventListener('click', () => { 362 + const checkoutId = jsContext.checkoutId; 363 + fetch(`/api/storefront/checkouts/${checkoutId}`, { 364 + method: 'GET', 365 + headers: { 366 + 'Content-Type': 'application/json', 367 + }, 368 + }) 369 + .then((response) => { 370 + if (!response.ok) { 371 + throw new Error('GET request failed'); 372 + } 373 + return response.json(); // Parse the JSON response 374 + }) 375 + .then((data) => { 376 + const deliveryInstructions = data.customerMessage; 377 + 378 + if (deliveryInstructions?.trim()) { 379 + paymentContinueButton.click(); 380 + } else { 381 + missingWarn.style.display = 'block'; 382 + } 383 + }) 384 + .catch((error) => { 385 + console.error('Error:', error); 386 + missingWarn.textContent = 387 + 'Something went wrong. Check if you have set a delivery date in shipping and try again.'; 388 + missingWarn.style.display = 'block'; 389 + }); 390 + }); 391 + });
assets/js/loyalty.js

This is a binary file and will not be displayed.

+1 -1
config.json
··· 1 1 { 2 - "name": "DEV Theme - Friend referral 24/07/2024", 2 + "name": "DEV Theme - Fix delivery dates 2", 3 3 "version": "6.10.0", 4 4 "template_engine": "handlebars_v4", 5 5 "meta": {
+347 -757
templates/pages/checkout.html
··· 1 - {{#partial "head"}} 1 + {{#partial "head"}} {{{ checkout.checkout_head }}} {{{ stylesheet 2 + '/assets/css/optimized-checkout.css' }}} {{ getFontsCollection }} 2 3 3 - {{{ checkout.checkout_head }}} 4 - {{{ stylesheet '/assets/css/optimized-checkout.css' }}} 5 - 6 - 7 - {{ getFontsCollection }} 8 - 9 4 <script type="text/javascript"> 10 - window.language = {{{langJson 'optimized_checkout'}}}; 11 - </script> 5 + window.language = {{{langJson 'optimized_checkout'}}}; 6 + </script> 12 7 13 - {{{head.scripts}}} 14 - 15 - 16 - {{/partial}} 8 + {{{head.scripts}}} {{/partial}} {{#partial "page"}} 17 9 18 - {{#partial "page"}} 19 - 20 - 21 - 22 - 23 10 <!-- UIkit CSS --> 24 - <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.15.14/dist/css/uikit.min.css" /> 25 - <link rel="stylesheet" href="https://use.typekit.net/drc3iaj.css"> 11 + <link 12 + rel="stylesheet" 13 + href="https://cdn.jsdelivr.net/npm/uikit@3.15.14/dist/css/uikit.min.css" 14 + /> 15 + <link rel="stylesheet" href="https://use.typekit.net/drc3iaj.css" /> 26 16 27 17 <!-- UIkit JS --> 28 18 <script src="https://cdn.jsdelivr.net/npm/uikit@3.15.14/dist/js/uikit.min.js"></script> 29 19 <script src="https://cdn.jsdelivr.net/npm/uikit@3.15.14/dist/js/uikit-icons.min.js"></script> 30 20 31 21 <!-- CALL MOST RECENT JQUERY LIBRARY --> 32 - <script src="https://code.jquery.com/jquery-3.6.2.min.js" integrity="sha256-2krYZKh//PcchRtd+H+VyyQoZ/e3EcrkxhM8ycwASPA=" crossorigin="anonymous"></script> 22 + <script 23 + src="https://code.jquery.com/jquery-3.6.2.min.js" 24 + integrity="sha256-2krYZKh//PcchRtd+H+VyyQoZ/e3EcrkxhM8ycwASPA=" 25 + crossorigin="anonymous" 26 + ></script> 33 27 <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script> 34 28 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script> 35 29 36 - <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous"> 37 - <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.min.css"> 30 + <link 31 + rel="stylesheet" 32 + href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" 33 + integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" 34 + crossorigin="anonymous" 35 + /> 36 + <link 37 + rel="stylesheet" 38 + href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.min.css" 39 + /> 38 40 41 + {{inject "checkoutId" cart_id}} 39 42 40 - <script src="{{cdn 'assets/js/custom.js'}}"></script> 43 + <script src="{{cdn 'assets/js/delivery.js'}}"></script> 44 + <script src="{{cdn 'assets/js/loyalty.js'}}"></script> 45 + 41 46 {{#if customer_group_name '!==' 'trade_blocked'}} 42 - <div id="optimised_main_chout"> 43 - <header class="checkoutHeader optimizedCheckout-header"> 44 - <div class="checkoutHeader-content"> 45 - <h1 class="is-srOnly">{{lang 'checkout.title'}}</h1> 46 - <h2 class="checkoutHeader-heading"> 47 - <a class="checkoutHeader-link" href="{{urls.home}}"> 48 - {{#if checkout.header_image}} 49 - <img alt="{{settings.store_logo.title}}" class="checkoutHeader-logo" id="logoImage" src="https://cdn11.bigcommerce.com/s-lh9wfk05w0/images/stencil/original/image-manager/d-logo.png" 47 + <div id="optimised_main_chout"> 48 + <header class="checkoutHeader optimizedCheckout-header"> 49 + <div class="checkoutHeader-content"> 50 + <h1 class="is-srOnly">{{lang 'checkout.title'}}</h1> 51 + <h2 class="checkoutHeader-heading"> 52 + <a class="checkoutHeader-link" href="{{urls.home}}"> 53 + {{#if checkout.header_image}} 54 + <img 50 55 alt="{{settings.store_logo.title}}" 51 - title="{{settings.store_logo.title}}"> 52 - {{!-- 53 - <img alt="{{settings.store_logo.title}}" class="checkoutHeader-logo" id="logoImage" src="{{ checkout.header_image }}"/> 54 - --}} 55 - 56 - {{ else }} 57 - <span class="header-logo-text">{{settings.store_logo.title}}</span> 58 - {{/if}} 59 - </a> 60 - </h2> 61 - </div> 62 - </header> 63 - {{{ checkout.checkout_content }}} 64 - </div> 56 + class="checkoutHeader-logo" 57 + id="logoImage" 58 + src="https://cdn11.bigcommerce.com/s-lh9wfk05w0/images/stencil/original/image-manager/d-logo.png" 59 + alt="{{settings.store_logo.title}}" 60 + title="{{settings.store_logo.title}}" 61 + /> 62 + {{!-- 63 + <img 64 + alt="{{settings.store_logo.title}}" 65 + class="checkoutHeader-logo" 66 + id="logoImage" 67 + src="{{ checkout.header_image }}" 68 + /> 69 + --}} {{ else }} 70 + <span class="header-logo-text" 71 + >{{settings.store_logo.title}}</span 72 + > 73 + {{/if}} 74 + </a> 75 + </h2> 76 + </div> 77 + </header> 78 + {{{ checkout.checkout_content }}} 79 + </div> 65 80 {{else}} 66 - <div class="container" style="height: 200px;display: flex;"> 67 - <p style="margin: 50px auto;width: 80%;text-align: center;border: 1px solid;height: 50px;padding: 10px 5px;">You are currently blocked for shopping, Please contact to admin for more info</p> 68 - </div> 69 - {{/if}} 70 - <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBhEVGvTFe26KQ1-eDZ9i10znl03qH1ZlI&libraries=places,geometry&v=weekly" defer ></script> 71 - 72 - 81 + <div class="container" style="height: 200px; display: flex"> 82 + <p 83 + style=" 84 + margin: 50px auto; 85 + width: 80%; 86 + text-align: center; 87 + border: 1px solid; 88 + height: 50px; 89 + padding: 10px 5px; 90 + " 91 + >You are currently blocked for shopping, Please contact to admin for 92 + more info</p 93 + > 94 + </div> 95 + {{/if}} {{#if customer}} {{#unless customer_group_name '===' 'Trade'}} 73 96 <script> 74 - let mobileCheck = setInterval(mobileRename, 1000); 75 - function mobileRename() { 76 - const mobileModal = document.querySelector('.cartDrawer.optimizedCheckout-orderSummary') 77 - if (mobileModal) { 78 - console.log(mobileModal) 79 - mobileModal.querySelector('.cartDrawer-body a').textContent = "Show Details & Rewards" 80 - } 81 - } 82 - </script> 83 - {{inject "checkoutID" cart_id}} 84 - {{#if customer}} 85 - {{#unless customer_group_name '===' 'Trade'}} 86 - <script> 87 - var jsContext = JSON.parse({{jsContext}}); 97 + // Handles the loyalty points 98 + var jsContext = JSON.parse({{jsContext}}); 88 99 89 - let loyaltyInterval = setInterval(loyaltyCheck, 1000); 90 - function loyaltyCheck() { 91 - const elementParentType = document.querySelector('.layout-main') 92 - if (elementParentType) { 93 - const couponApply = document.querySelector('#applyRedeemableButton') 94 - const couponRemove = document.querySelector('[data-test="cart-price-callback"]') 95 - if (couponApply) { 96 - couponApply.addEventListener('click', () => { 97 - setTimeout(function () { 98 - window.location.reload() 99 - }, 1500); //delay is in milliseconds 100 - }) 101 - } 102 - if (couponRemove) { 103 - couponRemove.addEventListener('click', () => { 100 + let loyaltyInterval = setInterval(loyaltyCheck, 1000); 101 + function loyaltyCheck() { 102 + const elementParentType = document.querySelector('.layout-main') 103 + if (elementParentType) { 104 + const couponApply = document.querySelector('#applyRedeemableButton') 105 + const couponRemove = document.querySelector('[data-test="cart-price-callback"]') 106 + if (couponApply) { 107 + couponApply.addEventListener('click', () => { 108 + setTimeout(function () { 104 109 window.location.reload() 110 + }, 1500); //delay is in milliseconds 111 + }) 112 + } 113 + if (couponRemove) { 114 + couponRemove.addEventListener('click', () => { 115 + window.location.reload() 105 116 106 - }) 117 + }) 118 + } 119 + const nextSibling = elementParentType.nextElementSibling; 120 + // Check if mobile or desktop view 121 + if (nextSibling && (nextSibling.classList.contains('layout-cart'))) { 122 + // Desktop View 123 + // Check if desktop loyalty already exists 124 + const desktopLoyalty = document.querySelector('.cart-section.optimizedCheckout-orderSummary-cartSection.loyalty-points') 125 + if (!desktopLoyalty) { 126 + // define location and start func 127 + const element = document.querySelector('.cart.optimizedCheckout-orderSummary') 128 + loyaltyInitialize(element); 107 129 } 108 - const nextSibling = elementParentType.nextElementSibling; 109 - // Check if mobile or desktop view 110 - if (nextSibling && (nextSibling.classList.contains('layout-cart'))) { 111 - // Desktop View 112 - // Check if desktop loyalty already exists 113 - const desktopLoyalty = document.querySelector('.cart-section.optimizedCheckout-orderSummary-cartSection.loyalty-points') 114 - if (!desktopLoyalty) { 130 + } else if (nextSibling && (nextSibling.classList.contains('cartDrawer'))) { 131 + // Mobile View 132 + // Check if mobile modal is open 133 + const mobileModal = document.querySelector('.ReactModalPortal .modal') 134 + if (mobileModal) { 135 + // Check if mobile loyalty already exists 136 + const mobileLoyalty = document.querySelector('.modal-body .cart-section.optimizedCheckout-orderSummary-cartSection.loyalty-points') 137 + if (!mobileLoyalty) { 115 138 // define location and start func 116 - const element = document.querySelector('.cart.optimizedCheckout-orderSummary') 139 + const element = document.querySelector('.cart-modal-body.optimizedCheckout-orderSummary') 117 140 loyaltyInitialize(element); 118 141 } 119 - } else if (nextSibling && (nextSibling.classList.contains('cartDrawer'))) { 120 - // Mobile View 121 - // Check if mobile modal is open 122 - const mobileModal = document.querySelector('.ReactModalPortal .modal') 123 - if (mobileModal) { 124 - // Check if mobile loyalty already exists 125 - const mobileLoyalty = document.querySelector('.modal-body .cart-section.optimizedCheckout-orderSummary-cartSection.loyalty-points') 126 - if (!mobileLoyalty) { 127 - // define location and start func 128 - const element = document.querySelector('.cart-modal-body.optimizedCheckout-orderSummary') 129 - loyaltyInitialize(element); 130 - } 131 - } 132 142 } 133 143 } 134 144 } 145 + } 135 146 136 - function loyaltyInitialize(elementToAppend) { 137 - // Fetch loyalty points 138 - fetchLoyalty().then(points => { 139 - // Create a new div element for displaying loyalty points 140 - const pointsDiv = document.createElement('section'); 141 - const pointsDesc = document.createElement('p'); 142 - pointsDesc.id = 'pointsDesc' 143 - if (points === null) { 144 - pointsDesc.innerHTML = `Could not find loyalty points for this account`; 145 - } else { 146 - pointsDesc.innerHTML = `Loyalty points balance ${points}`; 147 - } 148 - pointsDiv.appendChild(pointsDesc); 149 - pointsDiv.className = 'cart-section optimizedCheckout-orderSummary-cartSection loyalty-points'; // Add a class name for styling if needed 150 - // Create a label and input for entering points to use 151 - const label = document.createElement('label'); 152 - label.setAttribute('for', 'points-input'); 153 - label.classList.add('loyaltyLabel') 147 + function loyaltyInitialize(elementToAppend) { 148 + // Fetch loyalty points 149 + fetchLoyalty().then(points => { 150 + // Create a new div element for displaying loyalty points 151 + const pointsDiv = document.createElement('section'); 152 + const pointsDesc = document.createElement('p'); 153 + pointsDesc.id = 'pointsDesc' 154 + if (points === null) { 155 + pointsDesc.innerHTML = `Could not find loyalty points for this account`; 156 + } else { 157 + pointsDesc.innerHTML = `Loyalty points balance ${points}`; 158 + } 159 + pointsDiv.appendChild(pointsDesc); 160 + pointsDiv.className = 'cart-section optimizedCheckout-orderSummary-cartSection loyalty-points'; // Add a class name for styling if needed 161 + // Create a label and input for entering points to use 162 + const label = document.createElement('label'); 163 + label.setAttribute('for', 'points-input'); 164 + label.classList.add('loyaltyLabel') 154 165 155 - const confirmButton = document.createElement('button') 156 - confirmButton.textContent = "Apply discount"; 157 - confirmButton.id = "loyaltyPointsBtn"; 158 - confirmButton.className = "btn btn-primary"; 166 + const confirmButton = document.createElement('button') 167 + confirmButton.textContent = "Apply discount"; 168 + confirmButton.id = "loyaltyPointsBtn"; 169 + confirmButton.className = "btn btn-primary"; 159 170 160 - const input = document.createElement('input'); 161 - input.type = 'number'; 162 - input.id = 'points-input'; 163 - input.pattern = '[0-9]'; 164 - input.min = '0'; 165 - input.max = points; 166 - input.placeholder = 'How many?'; 167 - 168 - const cancelButton = document.createElement('button') 169 - cancelButton.textContent = "Cancel discount"; 170 - cancelButton.id = "cancelLoyaltyPointsBtn"; 171 - cancelButton.className = "btn btn-secondary"; 172 - 173 - cancelButton.addEventListener('click', () => { 174 - console.log("Received Click"); 175 - const ID = jsContext.checkoutID; 176 - fetch(`https://purrform-apps-027e.onrender.com/applyDiscountToCheckout?checkoutId=${ID}&loyaltyPointsUsed=0`) 177 - .then((response) => { 178 - cancelButton.setAttribute('disabled', 'true') 179 - cancelButton.textContent = 'Removing...' 180 - if (!response.ok) { 181 - // do something here to let the user know the discount failed 182 - cancelButton.removeAttribute('disabled') 183 - input.value = '' 184 - console.log('Fail') 185 - } 186 - cancelButton.removeAttribute('disabled') 187 - window.location.reload() 188 - console.log('Ok') 189 - }) 190 - .catch(err => { 171 + const input = document.createElement('input'); 172 + input.type = 'number'; 173 + input.id = 'points-input'; 174 + input.pattern = '[0-9]'; 175 + input.min = '0'; 176 + input.max = points; 177 + input.placeholder = 'How many?'; 178 + 179 + const cancelButton = document.createElement('button') 180 + cancelButton.textContent = "Cancel discount"; 181 + cancelButton.id = "cancelLoyaltyPointsBtn"; 182 + cancelButton.className = "btn btn-secondary"; 183 + 184 + cancelButton.addEventListener('click', () => { 185 + console.log("Received Click"); 186 + const ID = jsContext.checkoutID; 187 + fetch(`https://purrform-apps-027e.onrender.com/applyDiscountToCheckout?checkoutId=${ID}&loyaltyPointsUsed=0`) 188 + .then((response) => { 189 + cancelButton.setAttribute('disabled', 'true') 190 + cancelButton.textContent = 'Removing...' 191 + if (!response.ok) { 191 192 // do something here to let the user know the discount failed 192 193 cancelButton.removeAttribute('disabled') 193 - cancelButton.textContent = 'Remove Discount' 194 194 input.value = '' 195 - }) 195 + console.log('Fail') 196 + } 197 + cancelButton.removeAttribute('disabled') 198 + window.location.reload() 199 + console.log('Ok') 196 200 }) 197 - 201 + .catch(err => { 202 + // do something here to let the user know the discount failed 203 + cancelButton.removeAttribute('disabled') 204 + cancelButton.textContent = 'Remove Discount' 205 + input.value = '' 206 + }) 207 + }) 198 208 199 - // Add event listener to ensure the input does not exceed the maximum points 200 - input.addEventListener('input', function () { 201 - const value = parseInt(this.value, 10); 202 - if (value > points) { 203 - this.value = points; 204 - } 205 - }); 206 209 207 - // Append the label and input to the usePointsDiv 208 - pointsDiv.appendChild(label); 209 - pointsDiv.appendChild(input); 210 - pointsDiv.appendChild(confirmButton); 211 - if (document.querySelector('[data-test="cart-discount"]')) { 212 - pointsDiv.appendChild(cancelButton); 210 + // Add event listener to ensure the input does not exceed the maximum points 211 + input.addEventListener('input', function () { 212 + const value = parseInt(this.value, 10); 213 + if (value > points) { 214 + this.value = points; 213 215 } 214 - if (elementToAppend) { 215 - const duplicates = document.querySelectorAll('.cart-section.optimizedCheckout-orderSummary-cartSection.loyalty-points') 216 - if (duplicates.length === 0) { 217 - elementToAppend.appendChild(pointsDiv); 218 - generateNewSummary(points) 219 - } 216 + }); 217 + 218 + // Append the label and input to the usePointsDiv 219 + pointsDiv.appendChild(label); 220 + pointsDiv.appendChild(input); 221 + pointsDiv.appendChild(confirmButton); 222 + if (document.querySelector('[data-test="cart-discount"]')) { 223 + pointsDiv.appendChild(cancelButton); 224 + } 225 + if (elementToAppend) { 226 + const duplicates = document.querySelectorAll('.cart-section.optimizedCheckout-orderSummary-cartSection.loyalty-points') 227 + if (duplicates.length === 0) { 228 + elementToAppend.appendChild(pointsDiv); 229 + generateNewSummary(points) 220 230 } 221 - 222 - const loyaltyPointsBtn = document.querySelector('#loyaltyPointsBtn') 231 + } 232 + 233 + const loyaltyPointsBtn = document.querySelector('#loyaltyPointsBtn') 223 234 224 - if (loyaltyPointsBtn) { 225 - loyaltyPointsBtn.addEventListener('click', () => { 226 - console.log("Received Click"); 227 - if (input.value === '') { 228 - return; 229 - } 230 - const ID = jsContext.checkoutID; 231 - const loyaltyPoints = document.querySelector('#points-input').value 232 - if (loyaltyPoints === "") { 233 - console.log("The input is empty."); 234 - } 235 - if (loyaltyPoints) { 236 - console.log(loyaltyPoints) 237 - } 238 - fetch(`https://purrform-apps-027e.onrender.com/applyDiscountToCheckout?checkoutId=${ID}&loyaltyPointsUsed=${loyaltyPoints}`) 239 - .then((response) => { 240 - loyaltyPointsBtn.setAttribute('disabled', 'true') 241 - loyaltyPointsBtn.textContent = 'Processing' 242 - if (!response.ok) { 243 - // do something here to let the user know the discount failed 244 - loyaltyPointsBtn.removeAttribute('disabled') 245 - input.value = '' 246 - console.log('Fail') 247 - } 248 - loyaltyPointsBtn.removeAttribute('disabled') 249 - window.location.reload() 250 - console.log('Ok') 251 - }) 252 - .catch(err => { 235 + if (loyaltyPointsBtn) { 236 + loyaltyPointsBtn.addEventListener('click', () => { 237 + console.log("Received Click"); 238 + if (input.value === '') { 239 + return; 240 + } 241 + const ID = jsContext.checkoutID; 242 + const loyaltyPoints = document.querySelector('#points-input').value 243 + if (loyaltyPoints === "") { 244 + console.log("The input is empty."); 245 + } 246 + if (loyaltyPoints) { 247 + console.log(loyaltyPoints) 248 + } 249 + fetch(`https://purrform-apps-027e.onrender.com/applyDiscountToCheckout?checkoutId=${ID}&loyaltyPointsUsed=${loyaltyPoints}`) 250 + .then((response) => { 251 + loyaltyPointsBtn.setAttribute('disabled', 'true') 252 + loyaltyPointsBtn.textContent = 'Processing' 253 + if (!response.ok) { 253 254 // do something here to let the user know the discount failed 254 255 loyaltyPointsBtn.removeAttribute('disabled') 255 - loyaltyPointsBtn.textContent = 'Apply discount' 256 256 input.value = '' 257 - }) 258 - }); 259 - } 260 - }).catch(error => { 261 - console.error('Error fetching loyalty points:', error); 257 + console.log('Fail') 258 + } 259 + loyaltyPointsBtn.removeAttribute('disabled') 260 + window.location.reload() 261 + console.log('Ok') 262 + }) 263 + .catch(err => { 264 + // do something here to let the user know the discount failed 265 + loyaltyPointsBtn.removeAttribute('disabled') 266 + loyaltyPointsBtn.textContent = 'Apply discount' 267 + input.value = '' 268 + }) 262 269 }); 263 270 } 264 - 265 - function generateNewSummary(currentUserPoints) { 266 - currentUserPoints = Number(currentUserPoints); 271 + }).catch(error => { 272 + console.error('Error fetching loyalty points:', error); 273 + }); 274 + } 267 275 268 - const masterContainer = document.createElement('div'); 269 - const masterText = document.createElement('p'); 270 - masterText.classList.add("loyalty-summary") 271 - masterText.textContent = "Loyalty Points Summary" 272 - masterContainer.appendChild(masterText) 276 + function generateNewSummary(currentUserPoints) { 277 + currentUserPoints = Number(currentUserPoints); 273 278 274 - const discountEl = document.querySelector('[data-test="cart-discount"] [data-test="cart-price-value"]') 275 - const earningsEl = document.querySelector('[data-test="cart-subtotal"] [data-test="cart-price-value"]') 276 - let VAT = document.querySelector('[data-test="cart-taxes"] [data-test="cart-price-value"]') 277 - let couponDiscount = document.querySelector('[data-test="cart-coupon"] [data-test="cart-price-value"]') 278 - const existingDiv = document.getElementById('points-input'); 279 - let earningPriceValue 280 - let earningLoyalPointValue 279 + const masterContainer = document.createElement('div'); 280 + const masterText = document.createElement('p'); 281 + masterText.classList.add("loyalty-summary") 282 + masterText.textContent = "Loyalty Points Summary" 283 + masterContainer.appendChild(masterText) 281 284 282 - let discountPriceValue 283 - let discountLoyalPointValue 285 + const discountEl = document.querySelector('[data-test="cart-discount"] [data-test="cart-price-value"]') 286 + const earningsEl = document.querySelector('[data-test="cart-subtotal"] [data-test="cart-price-value"]') 287 + let VAT = document.querySelector('[data-test="cart-taxes"] [data-test="cart-price-value"]') 288 + let couponDiscount = document.querySelector('[data-test="cart-coupon"] [data-test="cart-price-value"]') 289 + const existingDiv = document.getElementById('points-input'); 290 + let earningPriceValue 291 + let earningLoyalPointValue 284 292 285 - if (discountEl) { 286 - // Discount Div 287 - const discountContainer = document.createElement('div'); 288 - discountContainer.classList.add('cart-summaryItem') 293 + let discountPriceValue 294 + let discountLoyalPointValue 289 295 290 - const discountName = document.createElement('div'); 291 - const discountValue = document.createElement('div'); 296 + if (discountEl) { 297 + // Discount Div 298 + const discountContainer = document.createElement('div'); 299 + discountContainer.classList.add('cart-summaryItem') 292 300 293 - discountName.textContent = "Points redeemed" 301 + const discountName = document.createElement('div'); 302 + const discountValue = document.createElement('div'); 294 303 295 - discountPriceValue = discountEl.innerHTML.replace(/[^\d.-]/g, '') 296 - discountPriceValue = discountPriceValue.replace(/[^\d.-]/g, ''); // Remove non-numeric characters 297 - discountPriceValue = Number(discountPriceValue); 298 - discountPriceValue = Math.abs(discountPriceValue); 299 - discountLoyalPointValue = discountPriceValue * 100 300 - discountLoyalPointValue = Math.floor(discountLoyalPointValue); 301 - 302 - discountValue.textContent = `-${discountLoyalPointValue}` 304 + discountName.textContent = "Points redeemed" 303 305 304 - discountContainer.appendChild(discountName) 305 - discountContainer.appendChild(discountValue) 306 - masterContainer.appendChild(discountContainer) 307 - } 308 - if (earningsEl) { 309 - // Earning Div 310 - const earningContainer = document.createElement('div'); 311 - earningContainer.classList.add('cart-summaryItem') 306 + discountPriceValue = discountEl.innerHTML.replace(/[^\d.-]/g, '') 307 + discountPriceValue = discountPriceValue.replace(/[^\d.-]/g, ''); // Remove non-numeric characters 308 + discountPriceValue = Number(discountPriceValue); 309 + discountPriceValue = Math.abs(discountPriceValue); 310 + discountLoyalPointValue = discountPriceValue * 100 311 + discountLoyalPointValue = Math.floor(discountLoyalPointValue); 312 312 313 - const earningName = document.createElement('div'); 314 - const earningValue = document.createElement('div'); 313 + discountValue.textContent = `-${discountLoyalPointValue}` 315 314 316 - earningName.textContent = "Points earned" 317 - earningPriceValue = earningsEl.innerHTML 318 - earningPriceValue = earningPriceValue.replace(/[^\d.-]/g, ''); // Remove non-numeric characters 319 - earningPriceValue = Number(earningPriceValue); // Use parseFloat to handle decimal values 320 - if (discountEl) { 321 - earningPriceValue = earningPriceValue - discountPriceValue 322 - } 323 - /* 324 - if (VAT) { 325 - VAT = Math.round(Number(VAT.innerHTML.replace(/[^\d.-]/g, ''))); 326 - earningPriceValue -= VAT; 327 - } 328 - */ 329 - if (couponDiscount) { 330 - earningPriceValue -= Math.floor(Math.abs(Number(couponDiscount.innerHTML.replace(/[^\d.-]/g, '')))); 331 - } 332 - 333 - earningLoyalPointValue = earningPriceValue / 2; 315 + discountContainer.appendChild(discountName) 316 + discountContainer.appendChild(discountValue) 317 + masterContainer.appendChild(discountContainer) 318 + } 319 + if (earningsEl) { 320 + // Earning Div 321 + const earningContainer = document.createElement('div'); 322 + earningContainer.classList.add('cart-summaryItem') 334 323 335 - earningLoyalPointValue = Math.floor(earningLoyalPointValue); 336 - if (earningLoyalPointValue < 0) { 337 - earningValue.textContent = `+0` 338 - } else { 339 - earningValue.textContent = `+${earningLoyalPointValue}` 340 - } 324 + const earningName = document.createElement('div'); 325 + const earningValue = document.createElement('div'); 341 326 342 - earningContainer.appendChild(earningName) 343 - earningContainer.appendChild(earningValue) 344 - masterContainer.appendChild(earningContainer) 327 + earningName.textContent = "Points earned" 328 + earningPriceValue = earningsEl.innerHTML 329 + earningPriceValue = earningPriceValue.replace(/[^\d.-]/g, ''); // Remove non-numeric characters 330 + earningPriceValue = Number(earningPriceValue); // Use parseFloat to handle decimal values 331 + if (discountEl) { 332 + earningPriceValue = earningPriceValue - discountPriceValue 345 333 } 346 - const finalContainer = document.createElement('div'); 347 - const finalResult = document.createElement('div'); 348 - const finalResultText = document.createElement('p'); 349 - 350 - const finalType = document.createElement('div'); 351 - const finalTypeText = document.createElement('p'); 352 - finalTypeText.textContent = 'Estimated total after purchase:' 353 - if (earningsEl && discountEl && currentUserPoints) { 354 - finalResultText.textContent = `${(currentUserPoints + earningLoyalPointValue - discountLoyalPointValue)}` 355 - } else if (earningsEl && currentUserPoints){ 356 - finalResultText.textContent = `${currentUserPoints + earningLoyalPointValue}` 357 - } else { 358 - finalResultText.textContent = `${earningLoyalPointValue}` 334 + /* 335 + if (VAT) { 336 + VAT = Math.round(Number(VAT.innerHTML.replace(/[^\d.-]/g, ''))); 337 + earningPriceValue -= VAT; 359 338 } 360 - finalType.appendChild(finalTypeText) 361 - finalResult.appendChild(finalResultText) 362 - finalContainer.appendChild(finalType) 363 - finalContainer.appendChild(finalResult) 364 - finalContainer.classList.add('cart-resultItem') 365 - 366 - masterContainer.appendChild(finalContainer) 367 - existingDiv.insertAdjacentElement('afterend', masterContainer); 368 - } 369 - 370 - // Append the usePointsDiv to the target element 371 - function fetchLoyalty() { 372 - return fetch('/graphql', { 373 - method: 'POST', 374 - credentials: 'same-origin', 375 - headers: { 376 - 'Content-Type': 'application/json', 377 - 'Authorization': 'Bearer {{ settings.storefront_api.token }}' 378 - }, 379 - body: JSON.stringify({ 380 - query: ` 381 - query loyaltyQuery{ 382 - customer { 383 - attributes { 384 - attribute(entityId: 1) { 385 - value 386 - name 387 - } 388 - } 389 - } 390 - } 391 - ` 392 - }), 393 - }) 394 - .then(res => res.json()) 395 - .then(json => { 396 - const attribute = json.data.customer.attributes.attribute; 397 - const points = attribute ? attribute.value : '0'; // Default to '0' if attribute is not found 398 - return points; 399 - }) 400 - .catch(error => { 401 - console.error('Error fetching loyalty points:', error); 402 - return '0'; // Return '0' in case of error 403 - }); 404 - } 405 - </script> 406 - {{/unless}} 407 - {{/if}} 339 + */ 340 + if (couponDiscount) { 341 + earningPriceValue -= Math.floor(Math.abs(Number(couponDiscount.innerHTML.replace(/[^\d.-]/g, '')))); 342 + } 408 343 409 - 410 - 411 - <script> 412 - // This script handles 'datepicker' in 'shipping' 413 - let shippingInterval = setInterval(shippingChecker, 1000); 414 - function shippingChecker() { 415 - const element = document.querySelector('#checkout-shipping-options'); 416 - const shippingMethod = document.querySelector('.form-checklist'); 417 - if (element && element !== null && shippingMethod) { 418 - clearInterval(shippingInterval); // Stop the interval once the element is found 419 - createCalendar(element) 420 - } 421 - } 422 - function createCalendar(element) { 423 - const existingInstructions = document.getElementById('datepicker-hoster'); 424 - if (existingInstructions) { 425 - existingInstructions.remove(); 426 - } 427 - // Define the HTML code for the elements 428 - const datePickerHTML = ` 429 - <div id="datepicker-hoster" class="datepicker"> 430 - <div class="cart-total-label"> 431 - <strong style="color: #687d6a;">Delivery Date *</strong> 432 - </div> 433 - <div class="voucher-section"> 434 - <input type="text" id="datepicker" name="datepicker" readonly autocomplete="off" aria-autocomplete="none" class="form-input" style="background: white; width: calc(100%); border-radius: 45px; border-color: #687d6a; color: #687d6a; font-size: 16px; text-align: center;" onkeydown="return false;"> 435 - <p id="datepicker_err" style="display: none; color: #363636; font-size: 16px; font-weight: normal;"></p> 436 - </div> 437 - <p style="font-size: 16px;">*Next Day delivery is usually available on any orders placed before 12 noon, Monday to Friday. Excluding some postcodes in Scotland.</p> 438 - </div> 439 - `; 440 - element.insertAdjacentHTML('beforeend', datePickerHTML); 441 - getCalendarData(); 442 - } 443 - // Get data for the calendar 444 - function getCalendarData() { 445 - // Create calendar and attach date picker logic here 446 - let dailyDeliveryLimit = 250; 447 - const dailySlotValues = {}; 448 - const disabledDates = []; 449 - // get delivery dates from middleware 450 - fetch('https://purrform-apps-027e.onrender.com/deliveryDates') 451 - .then((response) => { 452 - if (!response.ok) { 453 - console.log('Middleware network response was not ok'); 454 - throw new Error('Network response was not ok'); 455 - } 456 - return response.json(); 457 - }) 458 - .then((data) => { 459 - disabledDates.push(...data.unavailableDates); 460 - for (const [date, slots] of Object.entries(data.dateSlots)) { 461 - dailySlotValues[date] = slots; 462 - } 463 - if (data.perDay) { 464 - dailyDeliveryLimit = data.perDay; 465 - } 466 - attachDatePicker(dailyDeliveryLimit, dailySlotValues, disabledDates); 467 - }) 468 - .catch((error) => { 469 - console.log('Error calling delivery dates middleware endpoint:', error); 470 - // Handle error if necessary 471 - }); 472 - } 473 - // Attach 474 - function attachDatePicker(dailyDeliveryLimit, dailySlotValues, holidays) { 475 - 344 + earningLoyalPointValue = earningPriceValue / 2; 476 345 477 - function disableDates(date) { 478 - let result = [true, '', '']; // Default result to ensure proper return format 479 - const ulElement = document.querySelector('.form-checklist'); 480 - if (ulElement) { 481 - const liElements = ulElement.querySelectorAll('li'); 482 - liElements.forEach(li => { 483 - const listItem = li.querySelector('.shippingOption-desc'); 484 - const listItemText = listItem.innerHTML; 485 - const isSaturday = listItemText.toLowerCase().includes('saturday'); 486 - // The initial 487 - if (li.classList.contains('form-checklist-item--selected')) { 488 - if (isSaturday) { 489 - result = enableSaturday(date); 490 - } else { 491 - result = enableWeekdays(date); 492 - } 493 - } 494 - // Clicker 495 - li.addEventListener('click', function() { 496 - // Check if the clicked LI does not have the 'form-checklist-item--selected' class 497 - if (!li.classList.contains('form-checklist-item--selected')) { 498 - // Print a message to the console based on the LI's inner HTML 499 - if (isSaturday) { 500 - result = enableSaturday(date); 501 - } else { 502 - result = enableWeekdays(date); 503 - } 504 - } 505 - }); 506 - }); 346 + earningLoyalPointValue = Math.floor(earningLoyalPointValue); 347 + if (earningLoyalPointValue < 0) { 348 + earningValue.textContent = `+0` 507 349 } else { 508 - result = enableWeekdays(date); 509 - } 510 - return result; 511 - } 512 - // Disable everything except Saturday 513 - function enableSaturday(date) { 514 - const dayBeingChecked = $.datepicker.formatDate('yy-mm-dd', date); 515 - const dayOfTheWeek = date.getDay(); 516 - const dayOfMonth = date.getDate(); 517 - let slotsForThisDay = dailySlotValues[dayBeingChecked] ?? dailyDeliveryLimit; 518 - if (slotsForThisDay > dailyDeliveryLimit) { 519 - slotsForThisDay = dailyDeliveryLimit; 520 - } 521 - // Enable 6th day of the week (Saturday) AND if slots is higher than 0 522 - let isSaturdayWithSlots 523 - isSaturdayWithSlots = (dayOfTheWeek === 6 && slotsForThisDay > 0 && slotsForThisDay > 0 && !holidays.includes(dayBeingChecked) ); 524 - if (sessionStorage.getItem('deliveryDate')) { 525 - sessionStorage.clear('deliveryDate') 526 - location.reload(); 527 - } 528 - $('#datepicker').val(''); 529 - return [isSaturdayWithSlots, '', `Slots available: ${slotsForThisDay}`]; 530 - } 531 - // Enable weekdays 532 - function enableWeekdays(date) { 533 - const dayBeingChecked = $.datepicker.formatDate('yy-mm-dd', date); 534 - const dayOfTheWeek = date.getDay(); 535 - let slotsForThisDay = dailySlotValues[dayBeingChecked] ?? dailyDeliveryLimit; 536 - if (slotsForThisDay > dailyDeliveryLimit) { 537 - slotsForThisDay = dailyDeliveryLimit; 538 - } 539 - // Enable if not 6 (Saturday) 0 (Sunday) AND 1 (Monday) 540 - let isEnabledDay 541 - isEnabledDay = (dayOfTheWeek !== 6 && dayOfTheWeek !== 0 && dayOfTheWeek !== 1 && slotsForThisDay > 0 && !holidays.includes(dayBeingChecked) ); 542 - if (sessionStorage.getItem('deliveryDate')) { 543 - sessionStorage.clear('deliveryDate') 544 - location.reload(); 350 + earningValue.textContent = `+${earningLoyalPointValue}` 545 351 } 546 - $('#datepicker').val(''); 547 - return [isEnabledDay, '', `Slots available: ${slotsForThisDay}`]; 548 - } 549 - // Check if the session token for delivery date has been generated 550 - if (sessionStorage.getItem('deliveryDate')) { 551 - const datepicker = document.querySelector('#datepicker'); 552 - const deliveryDate = sessionStorage.getItem('deliveryDate'); 553 - datepicker.value = deliveryDate; 554 - } 555 - 556 - $('#datepicker').datepicker({ 557 - beforeShowDay: disableDates, 558 - defaultDate: '+1D', 559 - minDate: '+1D', 560 - maxDate: '+21D', 561 - dateFormat: 'd MM, yy', 562 - showOtherMonths: true, 563 - firstDay: 1, 564 - placeholder: 'Select Date' 565 - }); 566 - $('#datepicker').attr('placeholder', 'Select Date'); 567 - } 568 - </script> 569 352 570 - <script> 571 - let warnInterval = setInterval(renameWarning, 1000) 572 - function renameWarning() { 573 - let warningMsg = document.querySelector('.shippingOptions-panel.optimizedCheckout-overlay p') 574 - let proceedButton = document.querySelector('#proceedButton') 575 - if (warningMsg) { 576 - warningMsg.textContent = "Please ensure all required fields have been completed to see delivery options" 577 - if (proceedButton && warningMsg.textContent == "Please ensure all required fields have been completed to see delivery options") { 578 - proceedButton.addEventListener('click', () => { 579 - warningMsg.style.color = "red"; 580 - }) 581 - } 353 + earningContainer.appendChild(earningName) 354 + earningContainer.appendChild(earningValue) 355 + masterContainer.appendChild(earningContainer) 582 356 } 583 - } 584 - 585 - </script> 586 - 587 - <script> 588 - // This script handles 'instructions' in 'shipping' 589 - let instructionInterval = setInterval(instructionCheck, 1000); 590 - function instructionCheck() { 591 - const element = document.querySelector('fieldset.form-fieldset[data-test="checkout-shipping-comments"]'); 592 - const shippingMethod = document.querySelector('.form-checklist'); 357 + const finalContainer = document.createElement('div'); 358 + const finalResult = document.createElement('div'); 359 + const finalResultText = document.createElement('p'); 593 360 594 - if (element) { 595 - clearInterval(instructionInterval); 596 - createInstructions(); 597 - } 598 - } 599 - function createInstructions() { 600 - const existingInstructions = document.getElementById('instruction-wrap'); 601 - if (existingInstructions) { 602 - existingInstructions.remove(); 603 - } 604 - // generate custom HTML 605 - const instructionsDiv = ` 606 - <div id="instruction-wrap" class="delivery_instruction_wrapper"> 607 - <div class="del_cont"> 608 - <label class="form-legend optimizedCheckout-headingSecondary">Delivery Details</label> 609 - <select class="form-select form-select--small" id="delivery_inst_tag" onchange="showTextarea()"> 610 - <option value="Will be in">Will be in</option> 611 - <option value="user_instruction">User instruction</option> 612 - </select> 613 - <div id="word_limit" style="text-align: right;padding: 3px 0;display:none">(30 characters)</div> 614 - <textarea style="display:none" name="delivery_user_text" id="delivery_user_text" rows="1" cols="40" class="form-input"></textarea> 615 - </div> 616 - <p id="warningMessage" class="warning" style="display:none;">Please fill the required field.</p> 617 - <div id="proceedButton">Continue</div> 618 - </div> 619 - `; 620 - // Select the div with class 'checkout-address' 621 - const instructionSpot = document.querySelector('fieldset.form-fieldset[data-test="checkout-shipping-comments"]'); 622 - // Append the HTML code to the 'checkout-instruction' div 623 - instructionSpot.insertAdjacentHTML('afterend', instructionsDiv); 624 - } 625 - function showTextarea() { 626 - const selectElement = document.getElementById('delivery_inst_tag'); 627 - const textarea = document.getElementById('delivery_user_text'); 628 - if (selectElement.value === 'user_instruction') { 629 - textarea.style.display = 'block'; 361 + const finalType = document.createElement('div'); 362 + const finalTypeText = document.createElement('p'); 363 + finalTypeText.textContent = 'Estimated total after purchase:' 364 + if (earningsEl && discountEl && currentUserPoints) { 365 + finalResultText.textContent = `${(currentUserPoints + earningLoyalPointValue - discountLoyalPointValue)}` 366 + } else if (earningsEl && currentUserPoints){ 367 + finalResultText.textContent = `${currentUserPoints + earningLoyalPointValue}` 630 368 } else { 631 - textarea.style.display = 'none'; 369 + finalResultText.textContent = `${earningLoyalPointValue}` 632 370 } 633 - } 634 - </script> 371 + finalType.appendChild(finalTypeText) 372 + finalResult.appendChild(finalResultText) 373 + finalContainer.appendChild(finalType) 374 + finalContainer.appendChild(finalResult) 375 + finalContainer.classList.add('cart-resultItem') 635 376 636 - <script> 637 - // This script handles 'Shipping' continue button 638 - let resetInterval = setInterval(resetCheck, 1000); 639 - function resetCheck() { 640 - // Button by BC 641 - const shipButton = document.querySelector('#checkout-shipping-continue'); 642 - // Mockup button 643 - const proceedButton = document.querySelector('#proceedButton') 644 - const shippingMethod = document.querySelector('.form-checklist'); 645 - if (shipButton && proceedButton && shippingMethod) { 646 - // Stop the interval once the shipButton is found 647 - clearInterval(resetInterval); 648 - // Execute the function 649 - resetFunction(shipButton, proceedButton); 650 - } 651 - } 652 - function resetFunction(shipButton, proceedButton) { 653 - // Add event for new button 654 - proceedButton.addEventListener('click', function() { 655 - let finalInstructions = ""; 656 - // Get session keys if they exist 657 - const deliveryDate = sessionStorage.getItem('deliveryDate'); 658 - const deliveryInstr = sessionStorage.getItem('tracked_input_text'); 659 - // Get datepicker 660 - const datepicker = document.querySelector('.datepicker') 661 - const datePickerValue = document.querySelector('#datepicker').value 662 - // Get instructions 663 - const instructionSelector = document.querySelector('#delivery_inst_tag'); 664 - const inputElement = document.querySelector('input[name="orderComment"]'); 665 - // Set URL 666 - const url ="/api/storefront/checkouts/{{cart_id}}"; 667 - // Get warning 668 - const warning = document.querySelector("#warningMessage"); 669 - // Check if user decided to add instructions 670 - if (instructionSelector.value === "user_instruction") { 671 - const textarea = document.querySelector('#delivery_user_text'); 672 - // If no instructions provided 673 - if (textarea.value.length == 0) { 674 - textarea.value = "Will be in"; 675 - } 676 - finalInstructions = textarea.value; 677 - } else { 678 - finalInstructions = instructionSelector.value; 679 - } 680 - // Fill instructions and data 681 - const fullInstructions = `${datePickerValue} | ${finalInstructions}`; 682 - const newData = {customerMessage: fullInstructions}; 683 - //// Set and Put 684 - // Set input 685 - inputElement.value = fullInstructions; 686 - // Put request 687 - if (!datePickerValue == '' && !shipButton.disabled) { 688 - fetch("/api/storefront/checkouts/{{cart_id}}", { 689 - method: 'PUT', 690 - headers: { 691 - 'Content-Type': 'application/json' 692 - }, 693 - body: JSON.stringify(newData) 694 - }) 695 - .then(response => { 696 - if (!response.ok) { 697 - throw new Error('PUT request failed'); 698 - } 699 - return response.json(); // Parse the JSON response 700 - }) 701 - .then(data => { 702 - console.log('PUT request successful'); 703 - }) 704 - .catch(error => { 705 - console.error('Error:', error); 706 - }); 707 - // If delivery date is different 708 - if (deliveryDate) { 709 - if (deliveryDate !== datePickerValue) { 710 - // console.log('Date did not match') 711 - location.reload(); 712 - } 713 - } 714 - // If delivery instructions are different 715 - if (deliveryInstr) { 716 - if (finalInstructions !== deliveryInstr) { 717 - // console.log('Instructions did not match') 718 - location.reload(); 719 - } 720 - } 721 - // Assign new storage 722 - sessionStorage.setItem('tracked_input_text', finalInstructions); 723 - sessionStorage.setItem('deliveryDate', datePickerValue); 724 - // Remove warnings 725 - warning.style.display = "none" 726 - datepicker.classList.remove('warning') 727 - // Add delay to ensure put is ran 728 - setTimeout(function () { 729 - shipButton.click(); 730 - }, 1000); 731 - setTimeout(function () { 732 - resetInterval = setInterval(resetCheck, 1000); 733 - instructionInterval = setInterval(instructionCheck, 1000); 734 - shippingInterval = setInterval(shippingChecker, 1000); 735 - }, 2000); 736 - //User used shipping method but failed to fill in datepicker 737 - } else if (datePickerValue == '' && !shipButton.disabled) { 738 - warning.style.display = "block" 739 - warning.textContent = "Please fill in the datepicker field."; 740 - datepicker.classList.add('warning') 741 - // User used darepicker but failed to fill shipping method 742 - } else if (!datePickerValue == '' && shipButton.disabled) { 743 - warning.style.display = "block" 744 - warning.textContent = "Please select a shipping method."; 745 - } 746 - }); 377 + masterContainer.appendChild(finalContainer) 378 + existingDiv.insertAdjacentElement('afterend', masterContainer); 747 379 } 748 - </script> 749 380 750 - <script> 751 - // This script handles Payment Behaviour 752 - let finalInterval = setInterval(finalCheck, 1000); 753 - function finalCheck() { 754 - const element = document.querySelector('#checkout-payment-continue') 755 - if (element && element.innerHTML !== null) { 756 - // Generate elements and append location 757 - const fakeButton = document.createElement("div"); 758 - const missingWarn = document.createElement("p") 759 - const location = document.querySelector(".checkout-step--payment .checkout-form .form-actions") 760 - // Generate placeholder button 761 - fakeButton.innerHTML = "Place Order"; 762 - fakeButton.id = 'fakeButton'; 763 - fakeButton.style.display = 'none'; 764 - // Generate warning text 765 - missingWarn.classList.add('warning'); 766 - missingWarn.textContent = "Cannot find delivery date. Please review your 'shipping' details."; 767 - missingWarn.style.display = "none"; 768 - // Append the button to the body of the HTML document 769 - if (!document.querySelector('#fakeButton')) { 770 - location.appendChild(missingWarn); 771 - location.appendChild(fakeButton); 772 - finalFunction(element, fakeButton, missingWarn); 773 - } 774 - } 775 - } 776 - function finalFunction(element, fakeButton, missingWarn) { 777 - // Make sure its targetting the right 'order' button 778 - let mimicInterval = setInterval(function () { 779 - const nearestTar = document.querySelector('#checkout-payment-continue') 780 - if (nearestTar) { 781 - // Clear interval 782 - clearInterval(mimicInterval); 783 - // Hide real, show fake just incase 784 - element.style.display = 'none' 785 - fakeButton.style.display = 'block' 786 - // Add event now that it has the correct target 787 - fakeButton.addEventListener('click', () => { 788 - fetch("/api/storefront/checkouts/{{cart_id}}", { 789 - method: 'GET', 790 - headers: { 791 - 'Content-Type': 'application/json' 792 - }, 793 - }) 794 - .then(response => { 795 - if (!response.ok) { 796 - throw new Error('PUT request failed'); 797 - } 798 - return response.json(); // Parse the JSON response 799 - }) 800 - .then(data => { 801 - const deliveryInstructions = data.customerMessage 802 - if (data.customerMessage && data.customerMessage.trim()) { 803 - nearestTar.click(); 804 - } else { 805 - missingWarn.style.display = "block"; 381 + // Append the usePointsDiv to the target element 382 + function fetchLoyalty() { 383 + return fetch('/graphql', { 384 + method: 'POST', 385 + credentials: 'same-origin', 386 + headers: { 387 + 'Content-Type': 'application/json', 388 + 'Authorization': 'Bearer {{ settings.storefront_api.token }}' 389 + }, 390 + body: JSON.stringify({ 391 + query: ` 392 + query loyaltyQuery{ 393 + customer { 394 + attributes { 395 + attribute(entityId: 1) { 396 + value 397 + name 398 + } 806 399 } 807 - }) 808 - .catch(error => { 809 - console.error('Error:', error); 810 - }); 811 - }) 812 - } 813 - }, 1000); 814 - 815 - // Add reset button incase user changes their mind 816 - const editButtons = document.querySelectorAll('.checkout-step .button--tertiary.button--tiny.optimizedCheckout-buttonSecondary') 817 - editButtons.forEach(editButton => { 818 - editButton.addEventListener('click', () => { 819 - // reset the interval 820 - finalInterval = setInterval(finalCheck, 1000); 821 - }) 400 + } 401 + } 402 + ` 403 + }), 404 + }) 405 + .then(res => res.json()) 406 + .then(json => { 407 + const attribute = json.data.customer.attributes.attribute; 408 + const points = attribute ? attribute.value : '0'; // Default to '0' if attribute is not found 409 + return points; 410 + }) 411 + .catch(error => { 412 + console.error('Error fetching loyalty points:', error); 413 + return '0'; // Return '0' in case of error 822 414 }); 823 415 } 824 - 825 416 </script> 417 + {{/unless}} {{/if}} 826 418 827 419 <style> 828 420 .optimizedCheckout-cart-modal .optimizedCheckout-orderSummary-cartSection { ··· 884 476 } 885 477 .modal.modal--afterOpen.optimizedCheckout-cart-modal .modal-body { 886 478 box-shadow: none; 887 - height: 100%; 479 + height: 100%; 888 480 padding-bottom: 0; 889 481 overflow-x: auto; 890 - overflow-y: auto; 482 + overflow-y: auto; 891 483 } 892 484 .loyalty-points { 893 485 display: flex; 894 486 flex-direction: column; 895 - background-color: #FFEDCD; 487 + background-color: #ffedcd; 896 488 } 897 489 898 490 #points-input { ··· 900 492 border-radius: 32px; 901 493 padding: 16px 12px; 902 494 } 903 - fieldset[data-test="checkout-shipping-comments"], 495 + fieldset[data-test='checkout-shipping-comments'], 904 496 #checkout-shipping-continue, 905 - input[name="orderComment"], 497 + input[name='orderComment'], 906 498 #checkout-payment-continue { 907 499 display: none; 908 500 } ··· 924 516 font-size: 16px; 925 517 background-color: #687d6a; 926 518 transition: all 0.15s ease; 927 - font-family: "Montserrat", Arial, Helvetica, sans-serif; 519 + font-family: 'Montserrat', Arial, Helvetica, sans-serif; 928 520 font-weight: 500; 929 521 padding: 14px 24px; 930 522 text-align: center; ··· 1108 700 } 1109 701 </style> 1110 702 1111 - {{/partial}} 1112 - 1113 - {{> layout/empty}} 703 + {{/partial}} {{> layout/empty}}