import $ from 'jquery'; import prestashop from 'prestashop'; import debounce from './components/debounce'; prestashop.cart = prestashop.cart || {}; prestashop.cart.active_inputs = null; const spinnerSelector = 'input[name="product-quantity-spin"]'; let hasError = false; let isUpdateOperation = false; let errorMsg = ''; const CheckUpdateQuantityOperations = { switchErrorStat: () => { /** * if errorMsg is not empty or if notifications are shown, we have error to display * if hasError is true, quantity was not updated : we don't disable checkout button */ const $checkoutBtn = $(prestashop.themeSelectors.checkout.btn); if ($(prestashop.themeSelectors.notifications.dangerAlert).length || (errorMsg !== '' && !hasError)) { $checkoutBtn.addClass('disabled'); } if (errorMsg !== '') { const strError = ` `; $(prestashop.themeSelectors.notifications.container).html(strError); errorMsg = ''; isUpdateOperation = false; if (hasError) { // if hasError is true, quantity was not updated : allow checkout $checkoutBtn.removeClass('disabled'); } } else if (!hasError && isUpdateOperation) { hasError = false; isUpdateOperation = false; $(prestashop.themeSelectors.notifications.container).html(''); $checkoutBtn.removeClass('disabled'); } }, checkUpdateOperation: (resp) => { /** * resp.hasError can be not defined but resp.errors not empty: quantity is updated but order cannot be placed * when resp.hasError=true, quantity is not updated */ const {hasError: hasErrorOccurred, errors: errorData} = resp; hasError = hasErrorOccurred ?? false; const errors = errorData ?? ''; // 1.7.2.x returns errors as string, 1.7.3.x returns array if (errors instanceof Array) { errorMsg = errors.join(' '); } else { errorMsg = errors; } isUpdateOperation = true; }, }; /** * Attach Bootstrap TouchSpin event handlers */ function createSpin() { $.each($(spinnerSelector), (index, spinner) => { $(spinner).TouchSpin({ verticalbuttons: true, verticalupclass: 'material-icons touchspin-up', verticaldownclass: 'material-icons touchspin-down', buttondown_class: 'btn btn-touchspin js-touchspin js-increase-product-quantity', buttonup_class: 'btn btn-touchspin js-touchspin js-decrease-product-quantity', min: parseInt($(spinner).attr('min'), 10), max: 1000000, }); }); $(prestashop.themeSelectors.touchspin).off('touchstart.touchspin'); CheckUpdateQuantityOperations.switchErrorStat(); } const preventCustomModalOpen = (event) => { if (window.shouldPreventModal) { event.preventDefault(); return false; } return true; }; $(document).ready(() => { const productLineInCartSelector = prestashop.themeSelectors.cart.productLineQty; const promises = []; prestashop.on('updateCart', () => { $(prestashop.themeSelectors.cart.quickview).modal('hide'); }); prestashop.on('updatedCart', () => { window.shouldPreventModal = false; $(prestashop.themeSelectors.product.customizationModal).on('show.bs.modal', (modalEvent) => { preventCustomModalOpen(modalEvent); }); createSpin(); }); createSpin(); const $body = $('body'); function isTouchSpin(namespace) { return namespace === 'on.startupspin' || namespace === 'on.startdownspin'; } function shouldIncreaseProductQuantity(namespace) { return namespace === 'on.startupspin'; } function findCartLineProductQuantityInput($target) { const $input = $target.parents(prestashop.themeSelectors.cart.touchspin).find(productLineInCartSelector); if ($input.is(':focus')) { return null; } return $input; } function camelize(subject) { const actionTypeParts = subject.split('-'); let i; let part; let camelizedSubject = ''; for (i = 0; i < actionTypeParts.length; i += 1) { part = actionTypeParts[i]; if (i !== 0) { part = part.substring(0, 1).toUpperCase() + part.substring(1); } camelizedSubject += part; } return camelizedSubject; } function parseCartAction($target, namespace) { if (!isTouchSpin(namespace)) { return { url: $target.attr('href'), type: camelize($target.data('link-action')), }; } const $input = findCartLineProductQuantityInput($target); if (!$input) { return false; } let cartAction = {}; if (shouldIncreaseProductQuantity(namespace)) { cartAction = { url: $input.data('up-url'), type: 'increaseProductQuantity', }; } else { cartAction = { url: $input.data('down-url'), type: 'decreaseProductQuantity', }; } return cartAction; } const abortPreviousRequests = () => { let promise; while (promises.length > 0) { promise = promises.pop(); promise.abort(); } }; const getTouchSpinInput = ($button) => $($button.parents(prestashop.themeSelectors.cart.touchspin).find('input')); $(prestashop.themeSelectors.product.customizationModal).on('show.bs.modal', (modalEvent) => { preventCustomModalOpen(modalEvent); }); const handleCartAction = (event) => { abortPreviousRequests(); window.shouldPreventModal = true; event.preventDefault(); const $target = $(event.currentTarget); const {dataset} = event.currentTarget; const cartAction = parseCartAction($target, event.namespace); const requestData = { ajax: '1', action: 'update', }; if (!cartAction) { return; } $.ajax({ url: cartAction.url, method: 'POST', data: requestData, dataType: 'json', beforeSend(jqXHR) { promises.push(jqXHR); }, }) .then((resp) => { const $quantityInput = getTouchSpinInput($target); CheckUpdateQuantityOperations.checkUpdateOperation(resp); $quantityInput.val(resp.quantity); // Refresh cart preview prestashop.emit('updateCart', { reason: dataset, resp, }); }) .fail((resp) => { prestashop.emit('handleError', { eventType: 'updateProductInCart', resp, cartAction: cartAction.type, }); }); }; $body.on('click', prestashop.themeSelectors.cart.actions, handleCartAction); function sendUpdateQuantityInCartRequest(updateQuantityInCartUrl, requestData, $target) { abortPreviousRequests(); window.shouldPreventModal = true; return $.ajax({ url: updateQuantityInCartUrl, method: 'POST', data: requestData, dataType: 'json', beforeSend(jqXHR) { promises.push(jqXHR); }, }) .then((resp) => { CheckUpdateQuantityOperations.checkUpdateOperation(resp); $target.val(resp.quantity); const dataset = ($target && $target.dataset) ? $target.dataset : resp; // Refresh cart preview prestashop.emit('updateCart', { reason: dataset, resp, }); }) .fail((resp) => { prestashop.emit('handleError', { eventType: 'updateProductQuantityInCart', resp, }); }); } function getQuantityChangeType($quantity) { return $quantity > 0 ? 'up' : 'down'; } function getRequestData(quantity) { return { ajax: '1', qty: Math.abs(quantity), action: 'update', op: getQuantityChangeType(quantity), }; } function updateProductQuantityInCart(event) { const $target = $(event.currentTarget); const updateQuantityInCartUrl = $target.data('update-url'); const baseValue = $target.attr('value'); // There should be a valid product quantity in cart const targetValue = $target.val(); /* eslint-disable */ if (targetValue != parseInt(targetValue, 10) || targetValue < 0 || isNaN(targetValue)) { window.shouldPreventModal = false; $target.val(baseValue); return; } /* eslint-enable */ // There should be a new product quantity in cart const qty = targetValue - baseValue; if (qty === 0) { return; } if (targetValue === '0') { $target.closest('.product-line-actions').find('[data-link-action="delete-from-cart"]').click(); } else { $target.attr('value', targetValue); sendUpdateQuantityInCartRequest(updateQuantityInCartUrl, getRequestData(qty), $target); } } $body.on('touchspin.on.stopspin', spinnerSelector, debounce(updateProductQuantityInCart)); $body.on('focusout keyup', productLineInCartSelector, (event) => { if (event.type === 'keyup') { if (event.keyCode === 13) { isUpdateOperation = true; updateProductQuantityInCart(event); } return false; } if (!isUpdateOperation) { updateProductQuantityInCart(event); } return false; }); const $timeoutEffect = 400; $body.on('hidden.bs.collapse', prestashop.themeSelectors.cart.promoCode, () => { $(prestashop.themeSelectors.cart.displayPromo).show($timeoutEffect); }); $body.on('click', prestashop.themeSelectors.cart.promoCodeButton, (event) => { event.preventDefault(); $(prestashop.themeSelectors.cart.promoCode).collapse('toggle'); }); $body.on('click', prestashop.themeSelectors.cart.displayPromo, (event) => { $(event.currentTarget).hide($timeoutEffect); }); $body.on('click', prestashop.themeSelectors.cart.discountCode, (event) => { event.stopPropagation(); const $code = $(event.currentTarget); const $discountInput = $(prestashop.themeSelectors.cart.discountName); $discountInput.val($code.text()); // Show promo code field $(prestashop.themeSelectors.cart.promoCode).collapse('show'); $(prestashop.themeSelectors.cart.displayPromo).hide($timeoutEffect); return false; }); });