wickes-css2 2.103.0-RG-1658-apply-dynamic-logic-gift-card.1 → 2.103.0-develop.1

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 (79) hide show
  1. package/Readme.md +3 -1
  2. package/build/css/components/card-product-banner.css +1 -1
  3. package/build/css/components/checkout-payment-details-v2.css +1 -1
  4. package/build/css/main.css +1 -1
  5. package/build/css/pages/page_checkout_delivery-new.css +1 -1
  6. package/build/css/pages/page_products-list-combined.css +1 -1
  7. package/build/css/pages/page_products-list.css +1 -1
  8. package/build/css/plp-main.css +1 -1
  9. package/build/js/account-members.min.js +1 -1
  10. package/build/js/add-project-list-id.min.js +1 -1
  11. package/build/js/address-book.min.js +1 -1
  12. package/build/js/basket.min.js +2 -2
  13. package/build/js/bundle.min.js +1 -1
  14. package/build/js/change-password.min.js +1 -1
  15. package/build/js/checkout.min.js +2 -2
  16. package/build/js/emulation.min.js +70 -268
  17. package/build/js/general.bundle.min.js +1 -1
  18. package/build/js/merged-checkout.min.js +2 -2
  19. package/build/js/page/components/order-summary.js +25 -42
  20. package/build/js/page/plp-cards-v2.js +15 -6
  21. package/build/js/page/plp-load-more.js +1 -1
  22. package/build/js/page/utils/validation.js +1 -46
  23. package/build/js/pdp.bundle.min.js +1 -1
  24. package/build/js/personal-details.min.js +1 -1
  25. package/build/js/plp.bundle.min.js +1 -1
  26. package/build/js/project-list.min.js +1 -44
  27. package/build/js/quiz.min.js +1 -1
  28. package/build/js/track-my-order.min.js +1 -1
  29. package/package.json +2 -2
  30. package/src/components/card_product_v2.hbs +10 -5
  31. package/src/components/card_sponsor_banner.hbs +8 -0
  32. package/src/components/card_sponsor_product.hbs +6 -0
  33. package/src/components/checkout-payment-details-v2.hbs +2 -3
  34. package/src/components/injected-content.hbs +1 -1
  35. package/src/data/data_search-results_v2.json +56 -141
  36. package/src/elements/form-row.hbs +1 -1
  37. package/src/elements/input.hbs +2 -31
  38. package/src/js/components/banner-placement-manager.js +258 -0
  39. package/src/js/components/general/notification.js +1 -2
  40. package/src/js/components/toggle-password-visibility.js +58 -0
  41. package/src/js/emulation/banner-placement-manager.js +53 -0
  42. package/src/js/emulation/checkout-payment-details.js +14 -23
  43. package/src/js/emulation/forms.js +2 -7
  44. package/src/js/page/components/order-summary.js +25 -42
  45. package/src/js/page/plp-cards-v2.js +15 -6
  46. package/src/js/page/plp-load-more.js +1 -1
  47. package/src/js/page/utils/validation.js +1 -46
  48. package/src/layouts/checkout.hbs +5 -1
  49. package/src/page_my-account_change-password.html +0 -1
  50. package/src/page_payment-details-with-gift-card.html +4 -7
  51. package/src/page_plp_v2.html +16 -6
  52. package/src/page_search-results.html +12 -2
  53. package/src/scss/components/card-product-banner.scss +91 -3
  54. package/src/scss/components/checkout-payment-details-v2.scss +0 -2
  55. package/src/scss/pages/page_checkout_delivery-new.scss +0 -26
  56. package/src/scss/pages/page_products-list-combined.scss +11 -0
  57. package/src/scss/pages/page_products-list.scss +8 -0
  58. package/build/img/giftcard.svg +0 -28
  59. package/build/js/gift-cards.min.js +0 -1
  60. package/build/js/page/components/gift-cards.js +0 -877
  61. package/build/js/page/components/toggle-password-visibility.js +0 -22
  62. package/build/js/page/utils/gift-cards-utils.js +0 -144
  63. package/build/js/page/utils/input-handling.js +0 -92
  64. package/build/js/page/utils/show-hide-input.js +0 -28
  65. package/build/js/toggle-password-visibility.min.js +0 -1
  66. package/src/components/gift-cards-hint.hbs +0 -9
  67. package/src/components/gift-cards.hbs +0 -90
  68. package/src/components/giftcard-chip.hbs +0 -23
  69. package/src/components/giftcard-summary.hbs +0 -6
  70. package/src/img/giftcard.svg +0 -28
  71. package/src/js/components/product-banner.js +0 -148
  72. package/src/js/emulation/checkout-data.js +0 -35
  73. package/src/js/emulation/gift-cards.js +0 -201
  74. package/src/js/page/components/gift-cards.js +0 -877
  75. package/src/js/page/components/toggle-password-visibility.js +0 -22
  76. package/src/js/page/utils/gift-cards-utils.js +0 -144
  77. package/src/js/page/utils/input-handling.js +0 -92
  78. package/src/js/page/utils/show-hide-input.js +0 -28
  79. package/src/scss/components/_gift-cards.scss +0 -361
@@ -1,877 +0,0 @@
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
- ZERO_BALANCE_MESSAGE,
19
- PAY_MESSAGE_BY_CODE
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',
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 code = err && typeof err.code === 'number' ? err.code : undefined;
328
- const msg = MESSAGE_BY_CODE[code] || MESSAGE_BY_CODE[-1];
329
- this.showBannerError($root, msg);
330
- });
331
- },
332
-
333
- handleVerifyResponse($root, res) {
334
- if (res && res.code === OK_CODE) {
335
- if (!Array.isArray(res.giftCards)) {
336
- this.showBannerError($root, MESSAGE_BY_CODE[-1]);
337
- return;
338
- }
339
- if (Number(res.giftCardTotal) === 0) {
340
- this.showBannerError($root, ZERO_BALANCE_MESSAGE);
341
- return;
342
- }
343
-
344
- this.pendingServerGiftCards = res.giftCards.slice();
345
-
346
- if (res.orderTotal != null || res.giftCardTotal != null) {
347
- const data = { total: res.orderTotal, giftCardApplied: res.giftCardTotal };
348
- updateOrderSummary(data, buildSummaryEqualMap(data), true);
349
- this.updateCtaButton($root, { total: res.orderTotal });
350
- this.toggleBillingDescrForZero($root, { total: res.orderTotal });
351
- this.summaryTotal = res.orderTotal;
352
- }
353
-
354
- this.resetForm($root);
355
- this.showSubmitSpinner($root);
356
- return;
357
- }
358
-
359
- const msg = (res && MESSAGE_BY_CODE[res.code]) || MESSAGE_BY_CODE[-1];
360
- this.showBannerError($root, msg);
361
- },
362
-
363
- updateHintVisibility($root) {
364
- const {
365
- chipList, giftcardRow, hint, panel, toggle, iconInfo, expandedAttr,
366
- summaryHint, chip, block, body
367
- } = this.el;
368
-
369
- const $row = $root.closest(giftcardRow);
370
- const $giftcard = $row.find(block).first();
371
- const count = $row.find(`${chipList} ${chip}`).length;
372
- const limit = this.maxChips || 3;
373
-
374
- const zeroTotal = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
375
-
376
- this.toggleAltPayments($giftcard, count === 0);
377
- this.toggleAltPaymentInfos($giftcard, count === 0);
378
-
379
- if (count > 0) {
380
- if (!this.altPaymentPanelsLocked) {
381
- this.showOnlyBillingAddress($giftcard);
382
- this.altPaymentPanelsLocked = true;
383
- } else {
384
- this.showOnlyBillingAddress($giftcard);
385
- }
386
- } else {
387
- if (!this.altPaymentPanelsLocked) {
388
- this.toggleAltPayments($giftcard, true);
389
- this.toggleAltPaymentInfos($giftcard, true);
390
- } else {
391
- this.showOnlyBillingAddress($giftcard);
392
- }
393
- }
394
-
395
- const $summary = this.ensureSummaryContainer($giftcard);
396
-
397
- if (count === 0) {
398
- $summary.hide();
399
- $giftcard.show();
400
- this.resetForm($giftcard);
401
- $giftcard.find(panel).prop('hidden', true);
402
- $giftcard.find(toggle).attr(expandedAttr, 'false').show();
403
- $giftcard.find(hint)[zeroTotal ? 'hide' : 'show']();
404
- this.updateCtaButton($giftcard, { total: this.summaryTotal });
405
- return;
406
- }
407
-
408
- this.setBillingLabelToCard($giftcard);
409
- $summary.show();
410
- $giftcard.hide();
411
- $giftcard.find(hint).hide();
412
-
413
- const ctx = buildHintContext(count, limit, zeroTotal);
414
- const html = giftCardsHint(ctx);
415
- const $old = $summary.find(summaryHint).first();
416
- const $new = $(html);
417
-
418
- if ($old.length) $old.replaceWith($new);
419
- else $summary.append($new);
420
-
421
-
422
- if (zeroTotal) {
423
- $summary.find(summaryHint).hide();
424
- }
425
-
426
- this.toggleCardDetailsForTotal($giftcard);
427
-
428
- if ($.fn.tooltip) {
429
- $summary.find(summaryHint).find(iconInfo).tooltip({ container: body });
430
- }
431
- },
432
-
433
- renderChipHTML({ id, maskedNumber, currency, amount }) {
434
- return giftCardChip({ id, maskedNumber, currency, amount });
435
- },
436
-
437
- toggleAltPaymentInfos($root, show) {
438
- const { checkoutContainer, billieInfo, klarnaInfo } = this.el;
439
- const $container = $root.closest(checkoutContainer);
440
-
441
- if (this.altPaymentInfosLocked) {
442
- show = false;
443
- }
444
-
445
- [$container.find(billieInfo), $container.find(klarnaInfo)].forEach(($panel) => {
446
- if ($panel && $panel.length) {
447
- $panel[show ? 'show' : 'hide']();
448
- }
449
- });
450
-
451
- if (!show) this.altPaymentInfosLocked = true;
452
- },
453
-
454
- toggleAltPayments($root, show) {
455
- const {
456
- paymentForm,
457
- billieField,
458
- klarnaField,
459
- clearpayField,
460
- appleField,
461
- googleField,
462
- paypalField,
463
- checkoutContainer,
464
- formRow,
465
- cardRadio,
466
- cardField,
467
- } = this.el;
468
-
469
- const isTotalZero = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
470
-
471
- const $paymentField = $root.closest(checkoutContainer).find(paymentForm);
472
-
473
- const paymentSelectors = [
474
- billieField,
475
- klarnaField,
476
- clearpayField,
477
- appleField,
478
- googleField,
479
- paypalField,
480
- ];
481
-
482
- paymentSelectors.forEach((sel) => {
483
- const $row = $paymentField.find(sel).closest(formRow);
484
- if ($row.length) {
485
- $row[show ? 'show' : 'hide']();
486
- }
487
- });
488
-
489
- const $cardRow = $paymentField.find(cardField).closest(formRow);
490
- if ($cardRow.length) {
491
- $cardRow[isTotalZero ? 'hide' : 'show']();
492
- }
493
-
494
- if (!show && !isTotalZero) {
495
- let $radio = $root.closest(checkoutContainer).find(cardRadio);
496
- if (!$radio.length) $radio = $(cardRadio);
497
- if ($radio.length) {
498
- $radio.prop('checked', true);
499
- }
500
- }
501
- },
502
-
503
- handleAddAnotherClick(e) {
504
- e.preventDefault();
505
- const { giftcardRow, chipList, chip, block, number, summaryHint } = this.el;
506
-
507
- const $row = $(e.currentTarget).closest(giftcardRow);
508
- const count = $row.find(`${chipList} ${chip}`).length;
509
- if (count >= this.maxChips) return;
510
-
511
- const $giftcard = $row.find(block).first();
512
- this.resetForm($giftcard);
513
- this.open($giftcard);
514
-
515
- $row.find(summaryHint).hide();
516
-
517
- setTimeout(() => {
518
- $giftcard.find(number).trigger('focus');
519
- }, 0);
520
- },
521
-
522
- handleChipCloseClick(e) {
523
- e.preventDefault();
524
- const {
525
- chip,
526
- giftcardRow,
527
- block,
528
- giftcardChipClose,
529
- chipList,
530
- loaderChip,
531
- } = this.el;
532
-
533
- const $clickedChip = $(e.currentTarget).closest(chip);
534
- const $row = $clickedChip.closest(giftcardRow);
535
- const $giftcard = $row.find(block).first();
536
- const $summary = this.ensureSummaryContainer($giftcard);
537
-
538
- const $chips = $summary.find(`${chipList} ${chip}`);
539
- const index = $chips.index($clickedChip);
540
- if (index < 0) return;
541
-
542
- let $loader = $row.children(loaderChip);
543
- if (!$loader.length) {
544
- appendLoader({ wrapper: $row });
545
- $loader = $row.children(loaderChip).last();
546
- }
547
-
548
- $row
549
- .find(giftcardChipClose)
550
- .css('pointer-events', 'none')
551
- .attr('aria-disabled', 'true');
552
-
553
- removeGiftCard($loader, index)
554
- .then((res) => this.handleRemoveResponse($giftcard, res))
555
- .catch((err) => {
556
- const code = err && typeof err.code === 'number' ? err.code : undefined;
557
- const msg = MESSAGE_BY_CODE[code] || MESSAGE_BY_CODE[-1];
558
- this.showBannerError($giftcard, msg);
559
- })
560
- .finally(() => {
561
- hideLoader($loader);
562
- $row.find(giftcardChipClose).css('pointer-events', '').removeAttr('aria-disabled');
563
- });
564
- },
565
-
566
- handleRemoveResponse($giftcard, res) {
567
- if (res && res.code === OK_CODE) {
568
- const giftCardsList = Array.isArray(res.giftCards) ? res.giftCards : [];
569
- const data = { total: res.orderTotal, giftCardApplied: res.giftCardTotal };
570
- this.summaryTotal = res.orderTotal;
571
-
572
- this.renderGiftCardsFromList($giftcard, giftCardsList, data);
573
-
574
- if (res.orderTotal != null || res.giftCardTotal != null) {
575
- updateOrderSummary(data, buildSummaryEqualMap(data), true);
576
- }
577
- this.updateCtaButton && this.updateCtaButton($giftcard, { total: res.orderTotal });
578
- this.toggleBillingDescrForZero && this.toggleBillingDescrForZero($giftcard, { total: res.orderTotal });
579
- return;
580
- }
581
-
582
- const msg = (res && MESSAGE_BY_CODE[res.code]) || MESSAGE_BY_CODE[-1];
583
- this.showBannerError($giftcard, msg);
584
- },
585
-
586
- ensureSummaryContainer($root) {
587
- const { summary, giftcardRow } = this.el;
588
- const $row = $root.closest(giftcardRow);
589
- let $summary = $row.find(summary).first();
590
-
591
- if (!$summary.length) {
592
- $summary = $(giftcardSummary({}));
593
- $row.append($summary);
594
- }
595
-
596
- return $summary;
597
- },
598
-
599
- getPageLoader() {
600
- const $pl = $(this.el.pageLoader);
601
- if (!$pl.parent().is('body')) $pl.appendTo('body');
602
- return $pl;
603
- },
604
-
605
- showPageLoader() {
606
- const $pl = this.getPageLoader();
607
- showLoader($pl);
608
- },
609
-
610
- updateCtaButton($scope, summaryData) {
611
- const { ctaSelector, btnDetails, btnText, checkoutContainer } = this.el;
612
- let $cta = $(ctaSelector);
613
- if (!$cta.length) {
614
- $cta = $scope.closest(checkoutContainer).find(ctaSelector);
615
- }
616
- if (!$cta.length) return;
617
-
618
- const $wrap = $cta.closest(btnDetails).length ? $cta.closest(btnDetails) : $cta;
619
- const $text = $wrap.find(btnText).first().length ? $wrap.find(btnText).first() : $wrap;
620
- const totalLike = summaryData?.total;
621
- const useGiftcard = isZeroAmount(totalLike);
622
-
623
- $text.text(useGiftcard ? this.messages.ctaGiftcardText : this.messages.ctaDefaultText);
624
- $wrap.attr('data-mode', useGiftcard ? 'giftcards' : 'card');
625
-
626
- if (useGiftcard) {
627
- $wrap.attr('type', 'button').removeAttr('form');
628
- } else {
629
- $wrap.attr('type', 'submit').attr('form', 'hopPaymentAddressPostForm');
630
- }
631
- },
632
-
633
- toggleBillingDescrForZero($scope, summaryData) {
634
- const $container = $scope.closest(this.el.checkoutContainer);
635
- const $descr = $container.find(this.el.checkoutPaymentFields);
636
- if (!$descr.length) return;
637
-
638
- const shouldHide = summaryData ? isZeroAmount(summaryData.total) : false;
639
- $descr[shouldHide ? 'hide' : 'show']();
640
- },
641
-
642
- getNumberValue($root) {
643
- const { number } = this.el;
644
- return String($root.find(number).val() || '').replace(/\D/g, '');
645
- },
646
-
647
- getPinValue($root) {
648
- const { pin } = this.el;
649
- return String($root.find(pin).val() || '').replace(/\D/g, '');
650
- },
651
-
652
- bindGiftcardCtaHandler() {
653
- const self = this;
654
-
655
- $(document).off('click.giftcard', this.el.btnEnterDetails);
656
- $(document).on('click.giftcard', this.el.btnEnterDetails, function (e) {
657
- const $btn = $(this);
658
- const mode = $btn.attr('data-mode');
659
-
660
- if (mode !== 'giftcards') return;
661
-
662
- e.preventDefault();
663
- e.stopPropagation();
664
- e.stopImmediatePropagation();
665
-
666
- const $scope = $btn.closest(self.el.checkoutContainer).find(self.el.block).first();
667
- const $root = $scope.length ? $scope : $(self.el.block).first();
668
-
669
- self.showPageLoader();
670
-
671
- payWithGiftCard()
672
- .then(({ url }) => {
673
- if (typeof url === 'string' && url) {
674
- window.location.href = url;
675
- }
676
- })
677
- .catch((error) => {
678
- if (Array.isArray(error?.giftCards)) {
679
- self.renderGiftCardsFromList($root, error.giftCards);
680
- }
681
-
682
- const numberCode = Number(error?.errorCode);
683
-
684
- if (Number.isFinite(numberCode) && PAY_MESSAGE_BY_CODE[numberCode]) {
685
- Wick.Notification.show({
686
- text: linkifyClickHere(PAY_MESSAGE_BY_CODE[numberCode]),
687
- container: '.globalMessages .container',
688
- type: 'error',
689
- });
690
- }
691
- })
692
- .finally(() => {
693
- const $pl = self.getPageLoader();
694
- hideLoader($pl);
695
- });
696
- });
697
- },
698
-
699
- showBannerError($root, message, title = '') {
700
- const { notificationRoot, giftCardInline, inlineHeader } = this.el;
701
- const $inline = $root.find(giftCardInline);
702
-
703
- $inline.find(notificationRoot).remove();
704
-
705
- const html = notificationTpl({
706
- classModifier: 'notification_error',
707
- error: true,
708
- withCloseBtn: true,
709
- title: title || undefined,
710
- text: linkifyClickHere(message)
711
- });
712
-
713
- $inline.find(inlineHeader).after(html);
714
- },
715
-
716
-
717
- hideBanner($root) {
718
- const { notification } = this.el;
719
- $root.find(notification).remove();
720
- },
721
-
722
- mapGiftToChipData($root, gift) {
723
- const $giftcard = $root.closest(this.el.block);
724
- const currency = $giftcard.data('currency') || '£';
725
- return {
726
- id: gift.id,
727
- maskedNumber: String(gift.maskedNumber || ''),
728
- currency,
729
- amount: Number(gift.amountToRedeem || 0),
730
- };
731
- },
732
-
733
- renderGiftCardsFromList($root, giftCards, summaryData) {
734
- const { chipList, loaderChip, chip } = this.el;
735
- const $summary = this.ensureSummaryContainer($root);
736
- const $list = $summary.find(chipList);
737
- $list.children(`${chip}:not(${loaderChip})`).remove();
738
-
739
- const html = (giftCards || [])
740
- .map((g) => this.renderChipHTML(this.mapGiftToChipData($root, g)))
741
- .join('');
742
-
743
- if (html) {
744
- $list.append(html);
745
- $summary.show();
746
- }
747
-
748
- this.updateHintVisibility($root, summaryData);
749
-
750
- if (Array.isArray(giftCards) && giftCards.length > 0) {
751
- this.setBillingLabelToCard($root);
752
- }
753
- },
754
-
755
- toggleCardDetailsForTotal($scope) {
756
- const { checkoutContainer, cardDetails, hiddenCard } = this.el;
757
- const $container = $scope.closest(checkoutContainer);
758
- const $cardDetails = $container.find(cardDetails);
759
- if (!$cardDetails.length) return;
760
-
761
- const zero = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
762
- if (zero) $cardDetails.addClass(hiddenCard);
763
- },
764
-
765
- setBillingLabelToCard($scope) {
766
- const $container = $scope.closest(this.el.checkoutContainer);
767
- const $billing = $container.find(this.el.billingAddress);
768
- if (!$billing.length) return;
769
-
770
- const $label = $billing.find('[for="card-name"]');
771
- if ($label.length) {
772
- $label.text('Name on card');
773
- }
774
- },
775
-
776
- showOnlyBillingAddress($root) {
777
- const { checkoutContainer, billingAddress, altPaymentRowsAttr, hiddenCard } = this.el;
778
- const $container = $root.closest(checkoutContainer);
779
- $container.find(billingAddress).removeClass(hiddenCard);
780
- $container.find(altPaymentRowsAttr).addClass(hiddenCard);
781
- this.toggleAltPaymentInfos($root, false);
782
- },
783
-
784
- useGlobalError($root, message) {
785
- if (window.Wick && Wick.Notification && typeof Wick.Notification.show === 'function') {
786
- const text = linkifyClickHere ? linkifyClickHere(message) : String(message || '');
787
- Wick.Notification.show({
788
- text,
789
- container: '.globalMessages .container',
790
- type: 'error',
791
- });
792
- }
793
- },
794
-
795
- bindEvents() {
796
- const { number, pin, toggle, closeBtn, giftcardBtn, jsGiftcardAdd, giftcardChipClose } = this.el;
797
-
798
- $(document).off('.giftcard');
799
-
800
- const handleNumberInput = createGcNumberInputHandler({
801
- fieldSelector: this.el.field,
802
- fieldErrorClass: this.el.fieldError,
803
- errorTextSelector: this.el.errorText,
804
- ariaInvalidAttr: this.el.ariaInvalidAttr,
805
- });
806
-
807
- const onNumberBlur = (e) =>
808
- handleGcNumberBlur(e, {
809
- fieldSelector: this.el.field,
810
- errorTextSelector: this.el.errorText,
811
- errorClass: this.el.fieldError,
812
- ariaInvalidAttr: this.el.ariaInvalidAttr,
813
- numberMessage: this.messages.numberMessage,
814
- });
815
-
816
- const onPinInput = (e) =>
817
- handleGcPinInput(e, {
818
- fieldSelector: this.el.field,
819
- errorTextSelector: this.el.errorText,
820
- errorClass: this.el.fieldError,
821
- ariaInvalidAttr: this.el.ariaInvalidAttr,
822
- });
823
-
824
- const onPinBlur = (e) =>
825
- handleGcPinBlur(e, {
826
- fieldSelector: this.el.field,
827
- errorTextSelector: this.el.errorText,
828
- errorClass: this.el.fieldError,
829
- ariaInvalidAttr: this.el.ariaInvalidAttr,
830
- pinMessage: this.messages.pinMessage,
831
- });
832
-
833
-
834
- $(document)
835
- .on('input.giftcard', number, handleNumberInput)
836
- .on('keypress.giftcard', number, digitsOnlyKeypress)
837
- .on('blur.giftcard', number, onNumberBlur);
838
-
839
- $(document)
840
- .on('input.giftcard', pin, onPinInput)
841
- .on('keypress.giftcard', pin, handleGcPinKeypress)
842
- .on('blur.giftcard', pin, onPinBlur);
843
-
844
- $(document)
845
- .on('click.giftcard', toggle, this.handleToggleClick.bind(this))
846
- .on('click.giftcard', closeBtn, this.handleCloseClick.bind(this))
847
- .on('click.giftcard', giftcardBtn, this.handleFormSubmit.bind(this))
848
- .on('click.giftcard', jsGiftcardAdd, this.handleAddAnotherClick.bind(this))
849
- .on('click.giftcard', giftcardChipClose, this.handleChipCloseClick.bind(this))
850
-
851
- this.bindGiftcardCtaHandler();
852
- },
853
-
854
- init(payload = {}) {
855
- const { field, pin, $blocks } = this.el;
856
- this.bindEvents();
857
- if ($blocks && $blocks.length) {
858
- const $firstBlock = $blocks.first();
859
- if (Array.isArray(payload.giftCards) && payload.giftCards.length) {
860
- this.renderGiftCardsFromList($firstBlock, payload.giftCards);
861
- }
862
-
863
- const code = payload.errorCode;
864
-
865
- if (Number.isFinite(code) && PAY_MESSAGE_BY_CODE[code]) {
866
- this.useGlobalError($firstBlock, PAY_MESSAGE_BY_CODE[code]);
867
- }
868
- }
869
-
870
- $blocks.each((index, block) => {
871
- const $pinField = $(block).find(pin).closest(field);
872
- if ($pinField.length) {
873
- initializeInputToggle($pinField);
874
- }
875
- });
876
- }
877
- };