diff --git a/ajax/frontend/redeem.php b/ajax/frontend/redeem.php index 0866ff8f0e28031c0e352f694592b3d9a78210dd..02f080a8f32945d945c73b939bde4d09374b48bb 100644 --- a/ajax/frontend/redeem.php +++ b/ajax/frontend/redeem.php @@ -1,7 +1,7 @@ <?php /** - * This file contains package_quiqqer_coupons_ajax_delete + * This file contains package_quiqqer_coupons_ajax_frontend_redeem */ use QUI\ERP\Coupons\Handler; @@ -13,6 +13,15 @@ * @param int $id - CouponCode ID * @return bool - success */ + +/** + * Redeem a CouponCode + * @param string $code - coupon code + * @param string $orderHash - Order hash + * + * @throws QUI\Exception + * @throws QUI\ERP\Coupons\CouponCodeException + */ QUI::$Ajax->registerFunction( 'package_quiqqer_coupons_ajax_frontend_redeem', function ($code, $orderHash) { @@ -23,20 +32,14 @@ function ($code, $orderHash) { } catch (QUI\ERP\Coupons\CouponCodeException $Exception) { QUI\System\Log::writeDebugException($Exception); - QUI::getMessagesHandler()->addError($Exception->getMessage()); - - return false; + throw $Exception; } catch (Exception $Exception) { QUI\System\Log::writeException($Exception); - QUI::getMessagesHandler()->addError( - QUI::getLocale()->get( - 'quiqqer/coupons', - 'message.ajax.general_error' - ) - ); - - return false; + throw new QUI\Exception([ + 'quiqqer/coupons', + 'message.ajax.general_error' + ]); } $Order = QUI\ERP\Order\Handler::getInstance()->getOrderByHash($orderHash); @@ -49,40 +52,28 @@ function ($code, $orderHash) { foreach ($discounts as $Discount) { if (!DiscountEvents::isDiscountUsableWithQuantity($Discount, $productCount)) { - QUI::getMessagesHandler()->addError( - QUI::getLocale()->get( - 'quiqqer/coupons', - 'exception.CouponCode.discounts_invalid' - ) - ); - - return false; + throw new QUI\Exception([ + 'quiqqer/coupons', + 'exception.CouponCode.discounts_invalid' + ]); } if ($Discount->getAttribute('scope') === QUI\ERP\Discount\Handler::DISCOUNT_SCOPE_GRAND_TOTAL) { if (!DiscountEvents::isDiscountUsableWithPurchaseValue($Discount, $sum)) { - QUI::getMessagesHandler()->addError( - QUI::getLocale()->get( - 'quiqqer/coupons', - 'exception.CouponCode.discounts_invalid' - ) - ); - - return false; + throw new QUI\Exception([ + 'quiqqer/coupons', + 'exception.CouponCode.discounts_invalid' + ]); } continue; } if (!DiscountEvents::isDiscountUsableWithPurchaseValue($Discount, $subSum)) { - QUI::getMessagesHandler()->addError( - QUI::getLocale()->get( - 'quiqqer/coupons', - 'exception.CouponCode.discounts_invalid' - ) - ); - - return false; + throw new QUI\Exception([ + 'quiqqer/coupons', + 'exception.CouponCode.discounts_invalid' + ]); } } @@ -104,8 +95,6 @@ function ($code, $orderHash) { if ($Order instanceof QUI\ERP\Order\OrderInProcess) { $CouponCode->addToOrder($Order); } - - return true; }, ['code', 'orderHash'] ); diff --git a/bin/frontend/classes/CouponCodes.js b/bin/frontend/classes/CouponCodes.js index cea109e7435069d43307de925ac0f89ba75d3917..d0076baeb5067a80b66e66018dd670fc84651ae2 100644 --- a/bin/frontend/classes/CouponCodes.js +++ b/bin/frontend/classes/CouponCodes.js @@ -32,7 +32,8 @@ define('package/quiqqer/coupons/bin/frontend/classes/CouponCodes', [ 'package': pkg, code: code, orderHash: orderHash, - onError: reject + onError: reject, + showError: false // disable quiqqer message in frontend }); }); } diff --git a/bin/frontend/controls/CouponCodeInput.css b/bin/frontend/controls/CouponCodeInput.css index cde86056a03e70eb6e4b278dd58378f2aad1dba5..ebe6fcb04f77566d746a8e960906787c7861cfcb 100644 --- a/bin/frontend/controls/CouponCodeInput.css +++ b/bin/frontend/controls/CouponCodeInput.css @@ -1,4 +1,6 @@ .quiqqer-coupons-couponcodeinput { + --_qui-error-text-color: var(--qui-error-text-color, red); + --_qui-error-input-border-color: var(--qui-error-input-border-color, red); container-type: inline-size; } @@ -14,13 +16,28 @@ } .quiqqer-coupons-couponcodeinput-input { - min-width: min(250px, 100cqi); + min-width: min(200px, 100cqi); } .quiqqer-coupons-remove { margin-left: 1rem; } +/* error handling */ +input.quiqqer-coupons-couponcodeinput-input[data-invalid] { + border-color: var(--_qui-error-input-border-color) +} + +.quiqqer-coupons-couponcodeinput__errorMsg:not([data-show]) { + display: none; +} + +:where(.quiqqer-coupons-couponcodeinput__errorMsg[data-show]) { + font-size: 0.875rem; + color: var(--_qui-error-text-color); + margin-top: 0.25rem; +} + /** * Simple Checkout */ diff --git a/bin/frontend/controls/CouponCodeInput.html b/bin/frontend/controls/CouponCodeInput.html index ccc1a194696773f940f043178b8bb74157c7db87..65b4dfc226fc93bb3fb7dd80d322fb8035443a54 100644 --- a/bin/frontend/controls/CouponCodeInput.html +++ b/bin/frontend/controls/CouponCodeInput.html @@ -4,11 +4,13 @@ </label> <div class="quiqqer-coupons-couponcodeinput__inputGroup"> <input class="quiqqer-coupons-couponcodeinput-input" - type="text" name="code-input" id="code-input" data-name="code-input" + type="text" name="code-input" id="code-input" data-ref="code-input" placeholder="{{labelInputPlaceholder}}" + autocomplete="off" /> - <button class="quiqqer-coupons-couponcodeinput-btn btn btn-success" data-name="submit-coupon-code"> + <button class="quiqqer-coupons-couponcodeinput-btn btn btn-success" data-ref="submit-coupon-code"> {{submitBtnText}} </button> </div> + <div class="quiqqer-coupons-couponcodeinput__errorMsg" data-ref="errorMsg"></div> </div> \ No newline at end of file diff --git a/bin/frontend/controls/CouponCodeInput.js b/bin/frontend/controls/CouponCodeInput.js index 91b48117ec117a9bcf7f4c37cc7b3e549c58243d..df764ed84edf1bec1342f190168a916b39e88228 100644 --- a/bin/frontend/controls/CouponCodeInput.js +++ b/bin/frontend/controls/CouponCodeInput.js @@ -31,16 +31,19 @@ define('package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput', [ Type: 'package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput', Binds: [ - '$submit' + '$submit', + 'handleError' ], initialize: function(options) { this.parent(options); this.$Input = null; + this.$ErrorMsgContainer = null; this.Loader = new QUILoader(); this.$running = false; + this.addEvents({ onInject: this.$onInject }); @@ -65,17 +68,42 @@ define('package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput', [ submitBtnText: QUILocale.get(lg, lgPrefix + 'submitBtnText') })); + this.$ErrorMsgContainer = this.$Elm.querySelector('[data-ref="errorMsg"]'); + this.Loader.inject(this.$Elm); - this.$Input = this.$Elm.getElement('input[data-name="code-input"]'); + this.$Input = this.$Elm.getElement('[data-ref="code-input"]'); + + let timeoutId = null; + let previousValue = ''; + let currentValue = ''; + + const keyUpFunc = function(event ){ + currentValue = self.$Input.value.trim(); - this.$Input.addEvent('keyup', function(event) { if (event.code === 13) { + clearTimeout(timeoutId); + previousValue = currentValue; self.$submit(); + } else { + clearTimeout(timeoutId); + + timeoutId = setTimeout(function() { + if ( + (currentValue !== previousValue || currentValue === '') && + self.$ErrorMsgContainer.getAttribute('data-show') + ) { + self.hideErrorMessage(); + self.resetInvalidInput(); + } + previousValue = currentValue; + }, 400); } - }); + }; + + this.$Input.addEvent('keyup', keyUpFunc); - this.$Elm.getElement('[data-name="submit-coupon-code"]').addEvent('click', function(event) { + this.$Elm.getElement('[data-ref="submit-coupon-code"]').addEvent('click', function(event) { event.stop(); self.$submit(); }); @@ -153,12 +181,7 @@ define('package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput', [ this.$running = false; this.Loader.hide(); - }).catch((err) => { - console.error(err); - - this.$running = false; - this.Loader.hide(); - }); + }).catch(self.handleError); }, { 'package': 'quiqqer/order-simple-checkout', orderHash: orderHash @@ -184,7 +207,7 @@ define('package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput', [ 'package': 'quiqqer/order' }); }); - }); + }).catch(self.handleError); }, { 'package': 'quiqqer/order' }); @@ -197,18 +220,15 @@ define('package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput', [ OrderProcess.Loader.show(); OrderProcess.getOrder().then(function(orderHash) { - return CouponCodes.addCouponCodeToBasket(code, orderHash); - }).then(function(redeemed) { - self.$running = false; + CouponCodes.addCouponCodeToBasket(code, orderHash).then(() => { + self.$running = false; - if (redeemed === false) { + self.$addCouponCodeToSession(code).then(function() { + OrderProcess.reload(); + }); + }).catch((err) => { OrderProcess.Loader.hide(); - self.Loader.hide(); - return; - } - - self.$addCouponCodeToSession(code).then(function() { - OrderProcess.reload(); + self.handleError(err); }); }); }, @@ -285,6 +305,69 @@ define('package/quiqqer/coupons/bin/frontend/controls/CouponCodeInput', [ }); }); }); + }, + + /** + * Show error message container + * @param msg + */ + showErrorMessage: function( msg) { + if (!msg) { + console.log("no error message provided"); + + return; + } + + if (!this.$ErrorMsgContainer) { + return; + } + + this.$ErrorMsgContainer.set('html', msg); + this.$ErrorMsgContainer.setAttribute('data-show', '1'); + }, + + /** + * Hide error message container + */ + hideErrorMessage: function() { + if (!this.$ErrorMsgContainer) { + return; + } + + this.$ErrorMsgContainer.removeAttribute('data-show'); + }, + + /** + * Set data-invalid attribute to the input + */ + highlightInvalidInput: function() { + this.$Input.setAttribute('data-invalid', '1'); + }, + + /** + * Remove data-invalid attribute from the input + */ + resetInvalidInput: function() { + this.$Input.removeAttribute('data-invalid'); + }, + + /** + * Handle error message (show error message and highlight invalid input) + * + * @param err + */ + handleError: function(err) { + console.error(err); + + if (err.options !== undefined && err.options.message) { + const msg = err.options.message; + this.showErrorMessage(msg); + this.highlightInvalidInput(); + } + + this.$running = false; + this.Loader.hide(); } + }); });