wickes-css2 2.103.0-lg-788-no-extra-req-empty-cart.1 → 2.103.0-rg-1802-gift-card-error-codes.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/Readme.md +1 -2
  2. package/build/css/category-main.css +1 -1
  3. package/build/css/components/checkout-payment-details-v2.css +1 -1
  4. package/build/css/homepage-main.css +1 -1
  5. package/build/css/kitchen-plp-main.css +1 -1
  6. package/build/css/main.css +1 -1
  7. package/build/css/my-account-main-v2.css +1 -1
  8. package/build/css/my-account-main.css +1 -1
  9. package/build/css/pages/page_checkout_delivery-new.css +1 -1
  10. package/build/css/pages/page_product-details-v2.css +1 -1
  11. package/build/css/pdp-main-before-combine.css +1 -1
  12. package/build/css/pdp-main-non-critical.css +1 -1
  13. package/build/css/pdp-main.css +1 -1
  14. package/build/css/plp-main.css +1 -1
  15. package/build/css/store-locator-main.css +1 -1
  16. package/build/img/giftcard.svg +28 -0
  17. package/build/img/klarna.svg +10 -0
  18. package/build/img/payment/checkout/klarna.svg +10 -0
  19. package/build/img/payment/footer/klarna.svg +10 -0
  20. package/build/js/account-members.min.js +1 -1
  21. package/build/js/add-project-list-id.min.js +1 -1
  22. package/build/js/address-book.min.js +1 -1
  23. package/build/js/basket.min.js +2 -2
  24. package/build/js/bundle.min.js +1 -1
  25. package/build/js/change-password.min.js +1 -1
  26. package/build/js/checkout.min.js +2 -2
  27. package/build/js/emulation.min.js +282 -970
  28. package/build/js/general.bundle.min.js +1 -1
  29. package/build/js/gift-cards.min.js +1 -0
  30. package/build/js/merged-checkout.min.js +2 -2
  31. package/build/js/page/basket-v2.js +244 -138
  32. package/build/js/page/components/discounts.js +6 -6
  33. package/build/js/page/components/gift-cards.js +895 -0
  34. package/build/js/page/components/order-summary.js +42 -25
  35. package/build/js/page/components/toggle-password-visibility.js +22 -0
  36. package/build/js/page/utils/gift-cards-utils.js +183 -0
  37. package/build/js/page/utils/input-handling.js +92 -0
  38. package/build/js/page/utils/show-hide-input.js +28 -0
  39. package/build/js/page/utils/validation.js +46 -1
  40. package/build/js/pdp.bundle.min.js +1 -1
  41. package/build/js/personal-details.min.js +1 -1
  42. package/build/js/plp.bundle.min.js +1 -1
  43. package/build/js/project-list.min.js +44 -1
  44. package/build/js/quiz.min.js +1 -1
  45. package/build/js/toggle-password-visibility.min.js +1 -0
  46. package/build/js/track-my-order.min.js +1 -1
  47. package/package.json +2 -2
  48. package/src/components/checkout-payment-details-v2.hbs +10 -11
  49. package/src/components/checkout_payment-new.hbs +1 -4
  50. package/src/components/checkout_payment-other-methods.hbs +1 -4
  51. package/src/components/gift-cards-hint.hbs +9 -0
  52. package/src/components/gift-cards.hbs +90 -0
  53. package/src/components/giftcard-chip.hbs +23 -0
  54. package/src/components/giftcard-summary.hbs +6 -0
  55. package/src/components/mini-basket/mini-basket-empty.hbs +13 -3
  56. package/src/components/mini-basket/mini-basket.hbs +13 -32
  57. package/src/components/mini-basket/product-item.hbs +16 -37
  58. package/src/components/payments-checkout-v2.hbs +2 -7
  59. package/src/components/payments-checkout.hbs +1 -6
  60. package/src/components/payments.hbs +1 -5
  61. package/src/components/table_order-details-klarna.hbs +1 -1
  62. package/src/components/table_payment.hbs +1 -4
  63. package/src/data/data_confirmation-summary.json +2 -4
  64. package/src/data/data_mini-basket.json +80 -4
  65. package/src/data/data_wismo.json +1 -1
  66. package/src/elements/form-row.hbs +1 -1
  67. package/src/elements/input.hbs +31 -2
  68. package/src/img/giftcard.svg +28 -0
  69. package/src/img/klarna.svg +10 -0
  70. package/src/img/payment/checkout/klarna.svg +10 -0
  71. package/src/img/payment/footer/klarna.svg +10 -0
  72. package/src/js/components/general/cart-slider.js +0 -4
  73. package/src/js/components/general/create-popup-slider.js +2 -5
  74. package/src/js/components/general/mini-basket-slider.js +8 -0
  75. package/src/js/components/general/notification.js +2 -1
  76. package/src/js/emulation/checkout-data.js +35 -0
  77. package/src/js/emulation/checkout-payment-details.js +23 -14
  78. package/src/js/emulation/custom-slider-emulation.js +10 -4
  79. package/src/js/emulation/forms.js +7 -2
  80. package/src/js/emulation/gift-cards.js +205 -0
  81. package/src/js/page/basket-v2.js +244 -138
  82. package/src/js/page/components/discounts.js +6 -6
  83. package/src/js/page/components/gift-cards.js +895 -0
  84. package/src/js/page/components/order-summary.js +42 -25
  85. package/src/js/page/components/toggle-password-visibility.js +22 -0
  86. package/src/js/page/utils/gift-cards-utils.js +183 -0
  87. package/src/js/page/utils/input-handling.js +92 -0
  88. package/src/js/page/utils/show-hide-input.js +28 -0
  89. package/src/js/page/utils/validation.js +46 -1
  90. package/src/layouts/base.hbs +6 -0
  91. package/src/layouts/checkout.hbs +1 -5
  92. package/src/page_checkout_payment-details_guest-with-checkout-journey.html +1 -1
  93. package/src/page_my-account_change-password.html +1 -0
  94. package/src/page_my-order.html +1 -1
  95. package/src/page_payment-details-with-clearpay.html +2 -2
  96. package/src/page_payment-details-with-gift-card.html +8 -5
  97. package/src/page_shopping-cart-v2.html +2 -2
  98. package/src/partials/scripts.hbs +0 -1
  99. package/src/scss/components/_custom-slider.scss +3 -131
  100. package/src/scss/components/_gift-cards.scss +360 -0
  101. package/src/scss/components/_popover-mini-basket.scss +4 -0
  102. package/src/scss/components/checkout-payment-details-v2.scss +2 -0
  103. package/src/scss/pages/page_checkout_delivery-new.scss +26 -0
  104. package/src/scss/pages/page_product-details-v2.scss +4 -0
  105. package/src/sitemap.html +0 -6
  106. package/build/js/mini-basket-slider.min.js +0 -1
  107. package/build/js/page/basket/basket-update-cart-action.js +0 -59
  108. package/build/js/page/basket/basket-update-cart.js +0 -29
  109. package/build/js/page/basket/basket-utils.js +0 -50
  110. package/build/js/page/basket/mini-basket-total.js +0 -97
  111. package/build/js/page/basket/quantity-change-handler.js +0 -64
  112. package/build/js/page/basket/update-quantity-operation.js +0 -37
  113. package/build/js/page/basket/update-quantity.js +0 -65
  114. package/build/js/page/components/mini-basket-slider.js +0 -569
  115. package/src/components/mini-basket/mini-basket-order-item.hbs +0 -73
  116. package/src/js/components/toggle-password-visibility.js +0 -58
  117. package/src/js/emulation/mini-basket-data.js +0 -949
  118. package/src/js/page/basket/basket-update-cart-action.js +0 -59
  119. package/src/js/page/basket/basket-update-cart.js +0 -29
  120. package/src/js/page/basket/basket-utils.js +0 -50
  121. package/src/js/page/basket/mini-basket-total.js +0 -97
  122. package/src/js/page/basket/quantity-change-handler.js +0 -64
  123. package/src/js/page/basket/update-quantity-operation.js +0 -37
  124. package/src/js/page/basket/update-quantity.js +0 -65
  125. package/src/js/page/components/mini-basket-slider.js +0 -569
  126. package/src/page_split-orders-klarna.html +0 -106
@@ -0,0 +1,895 @@
1
+ import {initializeInputToggle} from "../utils/show-hide-input";
2
+ import {updateOrderSummary} from './order-summary';
3
+ import {hideLoader, showLoader, appendLoader} from "../utils/loader";
4
+ import {
5
+ ADD_GIFT_CARD,
6
+ buildHintContext,
7
+ buildSummaryEqualMap,
8
+ formatGc16,
9
+ isZeroAmount,
10
+ linkifyClickHere,
11
+ MESSAGE_BY_CODE,
12
+ OK_CODE,
13
+ PAY_WITH_GIFT_CARD,
14
+ payWithGiftCard,
15
+ REMOVE_GIFT_CARD,
16
+ removeGiftCard,
17
+ verifyGiftCard,
18
+ PAY_MESSAGE_BY_CODE,
19
+ DEFAULT_ERROR_MESSAGE
20
+ } from "../utils/gift-cards-utils";
21
+
22
+ import { clearErrorText, validateExactDigits } from "../utils/validation";
23
+ import {
24
+ createGcNumberInputHandler,
25
+ digitsOnlyKeypress,
26
+ handleGcNumberBlur,
27
+ handleGcPinInput,
28
+ handleGcPinKeypress,
29
+ handleGcPinBlur,
30
+ } from '../utils/input-handling';
31
+
32
+ const Handlebars = require('hbsfy/runtime');
33
+
34
+ Handlebars.registerPartial('info-icon', require("../../../components/info-icon.hbs"));
35
+ Handlebars.registerPartial('loader', require("../../../elements/loader.hbs"));
36
+
37
+ const giftCardsHint = require("../../../components/gift-cards-hint.hbs");
38
+ const giftCardChip = require("../../../components/giftcard-chip.hbs");
39
+ const giftcardSummary = require("../../../components/giftcard-summary.hbs");
40
+ const notificationTpl = require("../../../elements/notifications.hbs");
41
+
42
+ var Wick = window.Wick || {};
43
+
44
+ Wick.GiftCard = {
45
+ ADD_GIFT_CARD,
46
+ REMOVE_GIFT_CARD,
47
+ PAY_WITH_GIFT_CARD,
48
+ el: {
49
+ $blocks: $('.giftcard'),
50
+ block: '.giftcard',
51
+ toggle: '#giftcard-toggle',
52
+ panel: '#giftcard-inline',
53
+ hint: '.giftcard-hint',
54
+ closeBtn: '.giftcard-inline__close',
55
+ form: '.giftcard-inline__form',
56
+ number: 'input[name="giftcard-number"]',
57
+ pin: 'input[name="giftcard-pin"]',
58
+ note: '.giftcard-note',
59
+ field: '.giftcard__field',
60
+ errorText: '.giftcard__error-text',
61
+ fieldError: 'giftcard-field--error',
62
+ showToggle: '.toggle-show',
63
+ loader: '.giftcard__container .loader-wrapper',
64
+ success: '#giftcard-applied',
65
+ container: '.giftcard__container',
66
+ paymentForm: '.checkout-payment-details__payment-method',
67
+ billieField: '.form-row__field.form-row__field-billie',
68
+ klarnaField: '.form-row__field.form-row__field-klarna',
69
+ clearpayField: '.form-row__field.form-row__field-clearpay',
70
+ appleField: '.form-row__field.form-row__field-apple',
71
+ googleField: '.form-row__field.form-row__field-google',
72
+ paypalField: '.form-row__field.form-row__field-paypal',
73
+ cardField: '.form-row__field.form-row__card-payment',
74
+ summaryHint: '.giftcard-summary__hint',
75
+ chipList: '.giftcard-chip-list',
76
+ summary: '.giftcard-summary',
77
+ chip: '.giftcard-chip',
78
+ giftcardRow: '.giftcard-row',
79
+ checkoutContainer: '.checkout-payment-details',
80
+ formRow: '.form-row',
81
+ iconInfo: '.icon-info-component',
82
+ toggleIcon: 'svg, i',
83
+ iconEye: 'fa-eye',
84
+ iconEyeSlash: 'fa-eye-slash',
85
+ iconSvgEyeSlash: 'eye-slash',
86
+ passwordToggleEvent: 'input.passwordToggle',
87
+ ariaInvalidAttr: 'aria-invalid',
88
+ typeAttr: 'type',
89
+ dataIconAttr: 'data-icon',
90
+ svg: 'svg',
91
+ expandedAttr: 'aria-expanded',
92
+ giftcardBtn: '.giftcard__btn',
93
+ jsGiftcardAdd: '.js-giftcard-add',
94
+ giftcardChipClose: '.giftcard-chip__close',
95
+ body: 'body',
96
+ loaderChip: '.loader-wrapper',
97
+ chipListLoader: '.giftcard-chip-list > .loader-wrapper',
98
+ notification: '.giftcard-inline .notification',
99
+ giftCardInline: '.giftcard-inline',
100
+ notificationRoot: '.notification',
101
+ inlineHeader: '.giftcard-inline__header',
102
+ ctaSelector: '.btn-enter-details',
103
+ btnDetails: 'button',
104
+ btnText: '.btn__text',
105
+ btnEnterDetails: '.btn-enter-details',
106
+ checkoutPaymentFields: '.billing-address .checkout-payment-details__descr',
107
+ pageLoader: '.loader-wrapper.page-loader',
108
+ cardRadio: '#checkout-payment-details-card, #_checkout-payment-details-card_card',
109
+ billieInfo: '.checkout-payment-details__billie',
110
+ klarnaInfo: '.checkout-payment-details__klarna',
111
+ billingAddress: '.billing-address',
112
+ altPaymentRowsAttr: '[data-apple],[data-google],[data-paypal],[data-klarna],[data-billie],[data-clearpay],[data-existing-card]',
113
+ hiddenCard: 'checkout-payment-details__card-details_hidden',
114
+ cardDetails: '.checkout-payment-details__card-details',
115
+ },
116
+
117
+ messages: {
118
+ numberMessage: 'Gift Card should consist of 16 digits',
119
+ pinMessage: 'Gift Card PIN should consist of 8 digits',
120
+ ctaDefaultText: 'Enter card details',
121
+ ctaGiftcardText: 'Pay with Gift Card',
122
+ },
123
+
124
+ maxChips: 3,
125
+ successTimer: null,
126
+ pendingServerGiftCards: null,
127
+ altPaymentInfosLocked: false,
128
+ altPaymentPanelsLocked: false,
129
+ summaryTotal: null,
130
+
131
+ resetPinToggle($root) {
132
+ const {
133
+ pin,
134
+ field,
135
+ showToggle,
136
+ toggleIcon,
137
+ iconEye,
138
+ iconEyeSlash,
139
+ iconSvgEyeSlash,
140
+ passwordToggleEvent,
141
+ typeAttr,
142
+ dataIconAttr,
143
+ svg
144
+ } = this.el;
145
+
146
+ const $pinField = $root.find(pin).closest(field);
147
+ const $pinInput = $pinField.find(pin).first();
148
+
149
+ $pinInput.attr(typeAttr, 'password');
150
+
151
+ const $toggle = $pinField.find(showToggle).first();
152
+ const $icon = $toggle.find(toggleIcon).first();
153
+
154
+ if ($icon.length) {
155
+ $icon.removeClass(`${iconEye} ${iconEyeSlash}`).addClass(iconEyeSlash);
156
+ if ($icon.is(svg) && $icon.attr(dataIconAttr)) {
157
+ $icon.attr(dataIconAttr, iconSvgEyeSlash);
158
+ }
159
+ }
160
+
161
+ const hasValue = ($pinInput.val() || '').length > 0;
162
+ $toggle.toggle(hasValue);
163
+
164
+ $pinInput.trigger(passwordToggleEvent);
165
+ },
166
+
167
+ resetForm($root) {
168
+ const { number, pin, note } = this.el;
169
+ const $num = $root.find(number);
170
+ const $pinInput = $root.find(pin);
171
+
172
+ $num.val('');
173
+ $pinInput.val('');
174
+ clearErrorText($num, this.el.field, this.el.errorText, this.el.fieldError, this.el.ariaInvalidAttr);
175
+ clearErrorText($pinInput, this.el.field, this.el.errorText, this.el.fieldError, this.el.ariaInvalidAttr);
176
+ $root.find(note).remove();
177
+
178
+ this.hideBanner($root);
179
+ this.resetPinToggle($root);
180
+ },
181
+
182
+ open($root) {
183
+ const { toggle, panel, hint, pin, expandedAttr, passwordToggleEvent } = this.el;
184
+ this.hideBanner($root);
185
+ $root.show();
186
+
187
+ $root.find(panel).prop('hidden', false);
188
+ $root.find(toggle).attr(expandedAttr, 'true').hide();
189
+ $root.find(hint).hide();
190
+ $root.find(pin).trigger(passwordToggleEvent);
191
+ },
192
+
193
+ close($root) {
194
+ const { toggle, panel, loader, expandedAttr } = this.el;
195
+ const $toggle = $root.find(toggle);
196
+ const $panel = $root.find(panel);
197
+ const $loader = $root.find(loader);
198
+
199
+ showLoader($loader);
200
+
201
+ setTimeout(() => {
202
+ hideLoader($loader);
203
+
204
+ $panel.prop('hidden', true);
205
+ $toggle.attr(expandedAttr, 'false').show().trigger('focus');
206
+ this.updateHintVisibility($root);
207
+ }, 1000);
208
+ },
209
+
210
+ showSubmitSpinner($root) {
211
+ const { panel, toggle, hint, expandedAttr } = this.el;
212
+ $root.find(panel).prop('hidden', true);
213
+ $root.find(toggle).attr(expandedAttr, 'false').show().trigger('focus');
214
+ $root.find(hint).show();
215
+ this.showSuccess($root);
216
+ },
217
+
218
+ handleToggleClick(e) {
219
+ e.preventDefault();
220
+ this.open($(e.currentTarget).closest(this.el.$blocks));
221
+ },
222
+
223
+ handleCloseClick(e) {
224
+ e.preventDefault();
225
+ const $root = $(e.currentTarget).closest(this.el.$blocks);
226
+ this.resetForm($root);
227
+ this.close($root);
228
+ },
229
+
230
+ finalizeSuccess($root, $success) {
231
+ let { panel, toggle, hint, expandedAttr } = this.el;
232
+
233
+ if ($success && $success.length) {
234
+ $success.attr('hidden', 'hidden').removeClass('fade show').off('transitionend');
235
+ }
236
+
237
+ this.resetForm($root);
238
+ $root.find(panel).prop('hidden', true);
239
+ $root.find(toggle).attr(expandedAttr, 'false').show();
240
+ $root.find(hint).show();
241
+
242
+ if (this.pendingServerGiftCards) {
243
+ this.renderGiftCardsFromList($root, this.pendingServerGiftCards);
244
+ this.pendingServerGiftCards = null;
245
+ }
246
+
247
+ this.updateHintVisibility($root);
248
+ this.successTimer = null;
249
+ },
250
+
251
+ showSuccess($root) {
252
+ const { success} = this.el;
253
+
254
+ if (this.successTimer) {
255
+ clearTimeout(this.successTimer);
256
+ this.successTimer = null;
257
+ }
258
+
259
+ $root.hide();
260
+ const $success = $(success);
261
+ $success.removeAttr('hidden').addClass('fade');
262
+ $success[0] && $success[0].offsetWidth;
263
+ $success.addClass('show');
264
+
265
+ this.successTimer = setTimeout(() => {
266
+ $success.removeClass('show');
267
+ const handler = () => this.finalizeSuccess($root, $success);
268
+
269
+ $success.on('transitionend', handler);
270
+ setTimeout(handler, 200)
271
+ }, 3000);
272
+ },
273
+
274
+ handleFormSubmit(e) {
275
+ const { number, block, giftcardRow, loaderChip } = this.el;
276
+ e.preventDefault();
277
+
278
+ const $root = $(e.currentTarget).closest(block);
279
+ this.hideBanner($root);
280
+
281
+ const $num = $root.find(number);
282
+ $num.val(formatGc16($num.val()));
283
+
284
+ const $numInput = $root.find(this.el.number);
285
+ const $pinInput = $root.find(this.el.pin);
286
+
287
+ const numberBlurOpts = {
288
+ fieldSelector: this.el.field,
289
+ errorTextSelector: this.el.errorText,
290
+ errorClass: this.el.fieldError,
291
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
292
+ numberMessage: this.messages.numberMessage,
293
+ };
294
+
295
+ const pinBlurOpts = {
296
+ fieldSelector: this.el.field,
297
+ errorTextSelector: this.el.errorText,
298
+ errorClass: this.el.fieldError,
299
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
300
+ pinMessage: this.messages.pinMessage,
301
+ };
302
+
303
+ const okNumber = validateExactDigits($numInput, 16, this.messages.numberMessage, this.el);
304
+ const okPin = validateExactDigits($pinInput, 8, this.messages.pinMessage, this.el);
305
+
306
+ if (!(okNumber && okPin)) {
307
+ handleGcNumberBlur({ currentTarget: $numInput[0] }, numberBlurOpts);
308
+ handleGcPinBlur({ currentTarget: $pinInput[0] }, pinBlurOpts);
309
+ (!okNumber ? $numInput : $pinInput).trigger('focus');
310
+ return;
311
+ }
312
+
313
+ const giftCardNumber = this.getNumberValue($root);
314
+ const pin = this.getPinValue($root);
315
+ const $row = $root.closest(giftcardRow);
316
+
317
+ let $loader = $row.children(loaderChip);
318
+
319
+ if (!$loader.length) {
320
+ appendLoader({ wrapper: $row });
321
+ $loader = $row.children(loaderChip).last();
322
+ }
323
+
324
+ verifyGiftCard($loader, giftCardNumber, pin)
325
+ .then((res) => this.handleVerifyResponse($root, res))
326
+ .catch((err) => {
327
+ const msg = this.getErrorMessage(err);
328
+ this.showBannerError($root, msg);
329
+ });
330
+ },
331
+
332
+ handleVerifyResponse($root, res) {
333
+ if (res && res.code === OK_CODE) {
334
+ if (!Array.isArray(res.giftCards)) {
335
+ this.showBannerError($root, DEFAULT_ERROR_MESSAGE);
336
+ return;
337
+ }
338
+
339
+ this.pendingServerGiftCards = res.giftCards.slice();
340
+
341
+ if (res.orderTotal != null || res.giftCardTotal != null) {
342
+ const data = { total: res.orderTotal, giftCardApplied: res.giftCardTotal };
343
+ updateOrderSummary(data, buildSummaryEqualMap(data), true);
344
+ this.summaryTotal = res.orderTotal;
345
+ this.toggleBillingDescrForZero($root);
346
+ this.updateCtaButton($root);
347
+ }
348
+
349
+ this.resetForm($root);
350
+ this.showSubmitSpinner($root);
351
+ return;
352
+ }
353
+
354
+ const msg = (res && MESSAGE_BY_CODE[res.code]) || DEFAULT_ERROR_MESSAGE;
355
+ this.showBannerError($root, msg);
356
+ },
357
+
358
+ updateHintVisibility($root) {
359
+ const {
360
+ chipList, giftcardRow, hint, panel, toggle, iconInfo, expandedAttr,
361
+ summaryHint, chip, block, body
362
+ } = this.el;
363
+
364
+ const $row = $root.closest(giftcardRow);
365
+ const $giftcard = $row.find(block).first();
366
+ const count = $row.find(`${chipList} ${chip}`).length;
367
+ const limit = this.maxChips || 3;
368
+
369
+ const zeroTotal = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
370
+
371
+ this.toggleAltPayments($giftcard, count === 0);
372
+ this.toggleAltPaymentInfos($giftcard, count === 0);
373
+
374
+ if (count > 0) {
375
+ if (!this.altPaymentPanelsLocked) {
376
+ this.showOnlyBillingAddress($giftcard);
377
+ this.altPaymentPanelsLocked = true;
378
+ } else {
379
+ this.showOnlyBillingAddress($giftcard);
380
+ }
381
+ } else {
382
+ if (!this.altPaymentPanelsLocked) {
383
+ this.toggleAltPayments($giftcard, true);
384
+ this.toggleAltPaymentInfos($giftcard, true);
385
+ } else {
386
+ this.showOnlyBillingAddress($giftcard);
387
+ }
388
+ }
389
+
390
+ const $summary = this.ensureSummaryContainer($giftcard);
391
+
392
+ if (count === 0) {
393
+ $summary.hide();
394
+ $giftcard.show();
395
+ this.resetForm($giftcard);
396
+ $giftcard.find(panel).prop('hidden', true);
397
+ $giftcard.find(toggle).attr(expandedAttr, 'false').show();
398
+ $giftcard.find(hint)[zeroTotal ? 'hide' : 'show']();
399
+ this.updateCtaButton($giftcard);
400
+ return;
401
+ }
402
+
403
+ this.setBillingLabelToCard($giftcard);
404
+ $summary.show();
405
+ $giftcard.hide();
406
+ $giftcard.find(hint).hide();
407
+
408
+ const ctx = buildHintContext(count, limit, zeroTotal);
409
+ const html = giftCardsHint(ctx);
410
+ const $old = $summary.find(summaryHint).first();
411
+ const $new = $(html);
412
+
413
+ if ($old.length) $old.replaceWith($new);
414
+ else $summary.append($new);
415
+
416
+
417
+ if (zeroTotal) {
418
+ $summary.find(summaryHint).hide();
419
+ }
420
+
421
+ this.toggleCardDetailsForTotal($giftcard);
422
+
423
+ if ($.fn.tooltip) {
424
+ $summary.find(summaryHint).find(iconInfo).tooltip({ container: body });
425
+ }
426
+ },
427
+
428
+ renderChipHTML({ id, maskedNumber, currency, amount }) {
429
+ return giftCardChip({ id, maskedNumber, currency, amount });
430
+ },
431
+
432
+ toggleAltPaymentInfos($root, show) {
433
+ const { checkoutContainer, billieInfo, klarnaInfo } = this.el;
434
+ const $container = $root.closest(checkoutContainer);
435
+
436
+ if (this.altPaymentInfosLocked) {
437
+ show = false;
438
+ }
439
+
440
+ [$container.find(billieInfo), $container.find(klarnaInfo)].forEach(($panel) => {
441
+ if ($panel && $panel.length) {
442
+ $panel[show ? 'show' : 'hide']();
443
+ }
444
+ });
445
+
446
+ if (!show) this.altPaymentInfosLocked = true;
447
+ },
448
+
449
+ toggleAltPayments($root, show) {
450
+ const {
451
+ paymentForm,
452
+ billieField,
453
+ klarnaField,
454
+ clearpayField,
455
+ appleField,
456
+ googleField,
457
+ paypalField,
458
+ checkoutContainer,
459
+ formRow,
460
+ cardField,
461
+ } = this.el;
462
+
463
+ const $container = $root.closest(checkoutContainer);
464
+ const $paymentField = $container.find(paymentForm);
465
+ const isTotalZero = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
466
+
467
+ const paymentSelectors = [
468
+ billieField,
469
+ klarnaField,
470
+ clearpayField,
471
+ appleField,
472
+ googleField,
473
+ paypalField,
474
+ ];
475
+
476
+ paymentSelectors.forEach((sel) => {
477
+ const $row = $paymentField.find(sel).closest(formRow);
478
+ if ($row.length) {
479
+ const shouldShow = show && !isTotalZero;
480
+ $row[shouldShow ? 'show' : 'hide']();
481
+ }
482
+ });
483
+
484
+ const $cardRow = $paymentField.find(cardField).closest(formRow);
485
+ if ($cardRow.length) {
486
+ $cardRow[isTotalZero ? 'hide' : 'show']();
487
+ }
488
+
489
+ if (!show) {
490
+ this.ensureCardPaymentSelected($paymentField);
491
+ }
492
+ },
493
+
494
+ ensureCardPaymentSelected($paymentField) {
495
+ const { cardField, cardRadio, formRow } = this.el;
496
+
497
+ const $cardFieldRow = $paymentField.find(cardField).closest(formRow);
498
+ if (!$cardFieldRow.length) {
499
+ return;
500
+ }
501
+
502
+ const $radio = $cardFieldRow.find(cardRadio);
503
+
504
+ if ($radio.length && !$radio.prop('checked')) {
505
+ $radio.prop('checked', true);
506
+ $radio.trigger('change');
507
+ this.updateCtaButton($cardFieldRow);
508
+ }
509
+ },
510
+
511
+ handleAddAnotherClick(e) {
512
+ e.preventDefault();
513
+ const { giftcardRow, chipList, chip, block, number, summaryHint } = this.el;
514
+
515
+ const $row = $(e.currentTarget).closest(giftcardRow);
516
+ const count = $row.find(`${chipList} ${chip}`).length;
517
+ if (count >= this.maxChips) return;
518
+
519
+ const $giftcard = $row.find(block).first();
520
+ this.resetForm($giftcard);
521
+ this.open($giftcard);
522
+
523
+ $row.find(summaryHint).hide();
524
+
525
+ setTimeout(() => {
526
+ $giftcard.find(number).trigger('focus');
527
+ }, 0);
528
+ },
529
+
530
+ getErrorMessage(payload) {
531
+ const code =
532
+ payload && (typeof payload.code === 'number' || typeof payload.code === 'string')
533
+ ? payload.code
534
+ : undefined;
535
+
536
+ return (code != null && MESSAGE_BY_CODE[code]) || DEFAULT_ERROR_MESSAGE;
537
+ },
538
+
539
+ handleChipCloseClick(e) {
540
+ e.preventDefault();
541
+ const {
542
+ chip,
543
+ giftcardRow,
544
+ block,
545
+ giftcardChipClose,
546
+ chipList,
547
+ loaderChip,
548
+ } = this.el;
549
+
550
+ const $clickedChip = $(e.currentTarget).closest(chip);
551
+ const $row = $clickedChip.closest(giftcardRow);
552
+ const $giftcard = $row.find(block).first();
553
+ const $summary = this.ensureSummaryContainer($giftcard);
554
+
555
+ const $chips = $summary.find(`${chipList} ${chip}`);
556
+ const index = $chips.index($clickedChip);
557
+ if (index < 0) return;
558
+
559
+ let $loader = $row.children(loaderChip);
560
+ if (!$loader.length) {
561
+ appendLoader({ wrapper: $row });
562
+ $loader = $row.children(loaderChip).last();
563
+ }
564
+
565
+ $row
566
+ .find(giftcardChipClose)
567
+ .css('pointer-events', 'none')
568
+ .attr('aria-disabled', 'true');
569
+
570
+ removeGiftCard($loader, index)
571
+ .then((res) => this.handleRemoveResponse($giftcard, res))
572
+ .catch((err) => {
573
+ const msg = this.getErrorMessage(err);
574
+ this.showBannerError($giftcard, msg);
575
+ })
576
+ .finally(() => {
577
+ hideLoader($loader);
578
+ $row.find(giftcardChipClose).css('pointer-events', '').removeAttr('aria-disabled');
579
+ });
580
+ },
581
+
582
+ handleRemoveResponse($giftcard, res) {
583
+ if (res && res.code === OK_CODE) {
584
+ const giftCardsList = Array.isArray(res.giftCards) ? res.giftCards : [];
585
+ const data = { total: res.orderTotal, giftCardApplied: res.giftCardTotal };
586
+ this.summaryTotal = res.orderTotal;
587
+
588
+ this.renderGiftCardsFromList($giftcard, giftCardsList, data);
589
+
590
+ if (res.orderTotal != null || res.giftCardTotal != null) {
591
+ updateOrderSummary(data, buildSummaryEqualMap(data), true);
592
+ }
593
+ this.updateCtaButton($giftcard);
594
+ this.toggleBillingDescrForZero($giftcard);
595
+ return;
596
+ }
597
+
598
+ const msg = (res && MESSAGE_BY_CODE[res.code]) || DEFAULT_ERROR_MESSAGE;
599
+ this.showBannerError($giftcard, msg);
600
+ },
601
+
602
+ ensureSummaryContainer($root) {
603
+ const { summary, giftcardRow } = this.el;
604
+ const $row = $root.closest(giftcardRow);
605
+ let $summary = $row.find(summary).first();
606
+
607
+ if (!$summary.length) {
608
+ $summary = $(giftcardSummary({}));
609
+ $row.append($summary);
610
+ }
611
+
612
+ return $summary;
613
+ },
614
+
615
+ getPageLoader() {
616
+ const $pl = $(this.el.pageLoader);
617
+ if (!$pl.parent().is('body')) $pl.appendTo('body');
618
+ return $pl;
619
+ },
620
+
621
+ showPageLoader() {
622
+ const $pl = this.getPageLoader();
623
+ showLoader($pl);
624
+ },
625
+
626
+ updateCtaButton($scope) {
627
+ const { ctaSelector, btnDetails, btnText, checkoutContainer } = this.el;
628
+ let $cta = $(ctaSelector);
629
+ if (!$cta.length) {
630
+ $cta = $scope.closest(checkoutContainer).find(ctaSelector);
631
+ }
632
+ if (!$cta.length) return;
633
+
634
+ const $wrap = $cta.closest(btnDetails).length ? $cta.closest(btnDetails) : $cta;
635
+ const $text = $wrap.find(btnText).first().length ? $wrap.find(btnText).first() : $wrap;
636
+ const useGiftcard = isZeroAmount(this.summaryTotal);
637
+
638
+ $text.text(useGiftcard ? this.messages.ctaGiftcardText : this.messages.ctaDefaultText);
639
+ $wrap.attr('data-mode', useGiftcard ? 'giftcards' : 'card');
640
+
641
+ if (useGiftcard) {
642
+ $wrap.attr('type', 'button').removeAttr('form');
643
+ } else {
644
+ $wrap.attr('type', 'submit').attr('form', 'hopPaymentAddressPostForm');
645
+ }
646
+ },
647
+
648
+ toggleBillingDescrForZero($scope) {
649
+ const $container = $scope.closest(this.el.checkoutContainer);
650
+ const $descr = $container.find(this.el.checkoutPaymentFields);
651
+ if (!$descr.length) return;
652
+
653
+ const shouldHide = isZeroAmount(this.summaryTotal);
654
+ $descr[shouldHide ? 'hide' : 'show']();
655
+ },
656
+
657
+ getNumberValue($root) {
658
+ const { number } = this.el;
659
+ return String($root.find(number).val() || '').replace(/\D/g, '');
660
+ },
661
+
662
+ getPinValue($root) {
663
+ const { pin } = this.el;
664
+ return String($root.find(pin).val() || '').replace(/\D/g, '');
665
+ },
666
+
667
+ bindGiftcardCtaHandler() {
668
+ const self = this;
669
+
670
+ $(document).off('click.giftcard', this.el.btnEnterDetails);
671
+ $(document).on('click.giftcard', this.el.btnEnterDetails, function (e) {
672
+ const $btn = $(this);
673
+ const mode = $btn.attr('data-mode');
674
+
675
+ if (mode !== 'giftcards') return;
676
+
677
+ e.preventDefault();
678
+ e.stopPropagation();
679
+ e.stopImmediatePropagation();
680
+
681
+ const $scope = $btn.closest(self.el.checkoutContainer).find(self.el.block).first();
682
+ const $root = $scope.length ? $scope : $(self.el.block).first();
683
+
684
+ self.showPageLoader();
685
+
686
+ payWithGiftCard()
687
+ .then(({ url }) => {
688
+ if (typeof url === 'string' && url) {
689
+ window.location.href = url;
690
+ }
691
+ })
692
+ .catch((error) => {
693
+ if (Array.isArray(error?.giftCards)) {
694
+ self.renderGiftCardsFromList($root, error.giftCards);
695
+ }
696
+
697
+ const numberCode = Number(error?.errorCode);
698
+ const msg = (error.errorCode && PAY_MESSAGE_BY_CODE[error.errorCode]) || DEFAULT_ERROR_MESSAGE;
699
+
700
+ if (msg) {
701
+ Wick.Notification.show({
702
+ text: linkifyClickHere(PAY_MESSAGE_BY_CODE[numberCode]),
703
+ container: '.globalMessages .container',
704
+ type: 'error',
705
+ });
706
+ }
707
+ })
708
+ .finally(() => {
709
+ const $pl = self.getPageLoader();
710
+ hideLoader($pl);
711
+ });
712
+ });
713
+ },
714
+
715
+ showBannerError($root, message, title = '') {
716
+ const { notificationRoot, giftCardInline, inlineHeader } = this.el;
717
+ const $inline = $root.find(giftCardInline);
718
+
719
+ $inline.find(notificationRoot).remove();
720
+
721
+ const html = notificationTpl({
722
+ classModifier: 'notification_error',
723
+ error: true,
724
+ withCloseBtn: true,
725
+ title: title || undefined,
726
+ text: linkifyClickHere(message)
727
+ });
728
+
729
+ $inline.find(inlineHeader).after(html);
730
+ },
731
+
732
+
733
+ hideBanner($root) {
734
+ const { notification } = this.el;
735
+ $root.find(notification).remove();
736
+ },
737
+
738
+ mapGiftToChipData($root, gift) {
739
+ const $giftcard = $root.closest(this.el.block);
740
+ const currency = $giftcard.data('currency') || '£';
741
+ const rawAmount = Number(gift.amountToRedeem || 0);
742
+ const amount = Number.isFinite(rawAmount) ? rawAmount.toFixed(2) : '0.00';
743
+ return {
744
+ id: gift.id,
745
+ maskedNumber: String(gift.maskedNumber || ''),
746
+ currency,
747
+ amount,
748
+ };
749
+ },
750
+
751
+ renderGiftCardsFromList($root, giftCards, summaryData) {
752
+ const { chipList, loaderChip, chip } = this.el;
753
+ const $summary = this.ensureSummaryContainer($root);
754
+ const $list = $summary.find(chipList);
755
+ $list.children(`${chip}:not(${loaderChip})`).remove();
756
+
757
+ const html = (giftCards || [])
758
+ .map((g) => this.renderChipHTML(this.mapGiftToChipData($root, g)))
759
+ .join('');
760
+
761
+ if (html) {
762
+ $list.append(html);
763
+ $summary.show();
764
+ }
765
+
766
+ this.updateHintVisibility($root, summaryData);
767
+
768
+ if (Array.isArray(giftCards) && giftCards.length > 0) {
769
+ this.setBillingLabelToCard($root);
770
+ }
771
+ },
772
+
773
+ toggleCardDetailsForTotal($scope) {
774
+ const { checkoutContainer, cardDetails, hiddenCard } = this.el;
775
+ const $container = $scope.closest(checkoutContainer);
776
+ const $cardDetails = $container.find(cardDetails);
777
+ if (!$cardDetails.length) return;
778
+
779
+ const zero = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
780
+ if (zero) $cardDetails.addClass(hiddenCard);
781
+ },
782
+
783
+ setBillingLabelToCard($scope) {
784
+ const $container = $scope.closest(this.el.checkoutContainer);
785
+ const $billing = $container.find(this.el.billingAddress);
786
+ if (!$billing.length) return;
787
+
788
+ const $label = $billing.find('[for="card-name"]');
789
+ if ($label.length) {
790
+ $label.text('Name on card');
791
+ }
792
+ },
793
+
794
+ showOnlyBillingAddress($root) {
795
+ const { checkoutContainer, billingAddress, altPaymentRowsAttr, hiddenCard } = this.el;
796
+ const $container = $root.closest(checkoutContainer);
797
+ $container.find(billingAddress).removeClass(hiddenCard);
798
+ $container.find(altPaymentRowsAttr).addClass(hiddenCard);
799
+ this.toggleAltPaymentInfos($root, false);
800
+ },
801
+
802
+ useGlobalError($root, message) {
803
+ if (window.Wick && Wick.Notification && typeof Wick.Notification.show === 'function') {
804
+ const text = linkifyClickHere ? linkifyClickHere(message) : String(message || '');
805
+ Wick.Notification.show({
806
+ text,
807
+ container: '.globalMessages .container',
808
+ type: 'error',
809
+ });
810
+ }
811
+ },
812
+
813
+ bindEvents() {
814
+ const { number, pin, toggle, closeBtn, giftcardBtn, jsGiftcardAdd, giftcardChipClose } = this.el;
815
+
816
+ $(document).off('.giftcard');
817
+
818
+ const handleNumberInput = createGcNumberInputHandler({
819
+ fieldSelector: this.el.field,
820
+ fieldErrorClass: this.el.fieldError,
821
+ errorTextSelector: this.el.errorText,
822
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
823
+ });
824
+
825
+ const onNumberBlur = (e) =>
826
+ handleGcNumberBlur(e, {
827
+ fieldSelector: this.el.field,
828
+ errorTextSelector: this.el.errorText,
829
+ errorClass: this.el.fieldError,
830
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
831
+ numberMessage: this.messages.numberMessage,
832
+ });
833
+
834
+ const onPinInput = (e) =>
835
+ handleGcPinInput(e, {
836
+ fieldSelector: this.el.field,
837
+ errorTextSelector: this.el.errorText,
838
+ errorClass: this.el.fieldError,
839
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
840
+ });
841
+
842
+ const onPinBlur = (e) =>
843
+ handleGcPinBlur(e, {
844
+ fieldSelector: this.el.field,
845
+ errorTextSelector: this.el.errorText,
846
+ errorClass: this.el.fieldError,
847
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
848
+ pinMessage: this.messages.pinMessage,
849
+ });
850
+
851
+
852
+ $(document)
853
+ .on('input.giftcard', number, handleNumberInput)
854
+ .on('keypress.giftcard', number, digitsOnlyKeypress)
855
+ .on('blur.giftcard', number, onNumberBlur);
856
+
857
+ $(document)
858
+ .on('input.giftcard', pin, onPinInput)
859
+ .on('keypress.giftcard', pin, handleGcPinKeypress)
860
+ .on('blur.giftcard', pin, onPinBlur);
861
+
862
+ $(document)
863
+ .on('click.giftcard', toggle, this.handleToggleClick.bind(this))
864
+ .on('click.giftcard', closeBtn, this.handleCloseClick.bind(this))
865
+ .on('click.giftcard', giftcardBtn, this.handleFormSubmit.bind(this))
866
+ .on('click.giftcard', jsGiftcardAdd, this.handleAddAnotherClick.bind(this))
867
+ .on('click.giftcard', giftcardChipClose, this.handleChipCloseClick.bind(this))
868
+
869
+ this.bindGiftcardCtaHandler();
870
+ },
871
+
872
+ init(payload = {}) {
873
+ const { field, pin, $blocks } = this.el;
874
+ this.bindEvents();
875
+ if ($blocks && $blocks.length) {
876
+ const $firstBlock = $blocks.first();
877
+ if (Array.isArray(payload.giftCards) && payload.giftCards.length) {
878
+ this.renderGiftCardsFromList($firstBlock, payload.giftCards);
879
+ }
880
+
881
+ const code = payload.errorCode;
882
+
883
+ if (Number.isFinite(code) && PAY_MESSAGE_BY_CODE[code]) {
884
+ this.useGlobalError($firstBlock, PAY_MESSAGE_BY_CODE[code]);
885
+ }
886
+ }
887
+
888
+ $blocks.each((index, block) => {
889
+ const $pinField = $(block).find(pin).closest(field);
890
+ if ($pinField.length) {
891
+ initializeInputToggle($pinField);
892
+ }
893
+ });
894
+ }
895
+ };