wickes-css2 2.117.0-develop.3 → 2.117.0-gift-cards.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 (102) hide show
  1. package/Readme.md +0 -1
  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/checkout-new.css +1 -1
  10. package/build/css/pages/checkout.css +1 -1
  11. package/build/css/pages/page_my-order.css +1 -1
  12. package/build/css/pages/page_wismo-details.css +1 -1
  13. package/build/css/pdp-main-before-combine.css +1 -1
  14. package/build/css/pdp-main-critical.css +1 -1
  15. package/build/css/pdp-main-non-critical.css +1 -1
  16. package/build/css/pdp-main.css +1 -1
  17. package/build/css/plp-main.css +1 -1
  18. package/build/css/store-locator-main.css +1 -1
  19. package/build/img/payment/checkout/giftcard.svg +28 -0
  20. package/build/img/payment/footer/giftcard.svg +28 -0
  21. package/build/js/account-members.min.js +1 -1
  22. package/build/js/add-project-list-id.min.js +1 -1
  23. package/build/js/address-book.min.js +1 -1
  24. package/build/js/basket.min.js +2 -2
  25. package/build/js/bundle.min.js +1 -1
  26. package/build/js/change-password.min.js +1 -1
  27. package/build/js/checkout.min.js +2 -2
  28. package/build/js/emulation.min.js +424 -82
  29. package/build/js/general.bundle.min.js +1 -1
  30. package/build/js/gift-cards.min.js +1 -1
  31. package/build/js/merged-checkout.min.js +2 -2
  32. package/build/js/page/bloomreach-widget/bloomreach-widget-core.js +1 -3
  33. package/build/js/page/checkout-payment-details.js +114 -10
  34. package/build/js/page/components/gift-cards.js +524 -115
  35. package/build/js/page/components/postcode-lookup/postcode-lookup-api.js +56 -0
  36. package/build/js/page/components/postcode-lookup/postcode-lookup-render.js +98 -0
  37. package/build/js/page/components/postcode-lookup/postcode-lookup-validation.js +81 -0
  38. package/build/js/page/components/postcode-lookup.js +503 -0
  39. package/build/js/page/ripple/bulk-add.js +2 -2
  40. package/build/js/page/ripple/thumb-gallery.js +14 -8
  41. package/build/js/page/utils/address-utils.js +32 -0
  42. package/build/js/page/utils/validation.js +54 -1
  43. package/build/js/pdp.bundle.min.js +2 -2
  44. package/build/js/personal-details.min.js +1 -1
  45. package/build/js/plp.bundle.min.js +1 -1
  46. package/build/js/postcode-checker.min.js +1 -1
  47. package/build/js/project-list.min.js +57 -2
  48. package/build/js/quiz.min.js +1 -1
  49. package/build/js/track-my-order.min.js +1 -1
  50. package/package.json +5 -1
  51. package/src/components/checkout-payment-details-v2.hbs +1 -0
  52. package/src/components/checkout_order-summary-new.hbs +6 -0
  53. package/src/components/checkout_payment-new.hbs +35 -8
  54. package/src/components/gift-cards.hbs +8 -15
  55. package/src/components/order-address.hbs +28 -0
  56. package/src/components/payments.hbs +7 -6
  57. package/src/components/postcode-lookup-v2.hbs +85 -0
  58. package/src/components/wismo-giftcard-payment-item.hbs +29 -0
  59. package/src/components/wismo-payment.hbs +10 -0
  60. package/src/data/data_checkout-giftcards.json +7 -0
  61. package/src/data/data_delivery-address-v2.json +4 -0
  62. package/src/data/data_payments-checkout.json +4 -0
  63. package/src/data/data_postcode-lookup.json +200 -0
  64. package/src/data/data_wismo.json +46 -1
  65. package/src/data/footer_menu.json +4 -0
  66. package/src/elements/input.hbs +3 -1
  67. package/src/img/payment/checkout/giftcard.svg +28 -0
  68. package/src/img/payment/footer/giftcard.svg +28 -0
  69. package/src/js/components/general/footer.js +63 -56
  70. package/src/js/emulation/checkout-payment-details.js +2 -0
  71. package/src/js/emulation/gift-cards.js +22 -0
  72. package/src/js/emulation/mock.js +21 -20
  73. package/src/js/emulation/postcode-checker.js +1 -1
  74. package/src/js/emulation/postcode-lookup.js +122 -0
  75. package/src/js/page/bloomreach-widget/bloomreach-widget-core.js +1 -3
  76. package/src/js/page/checkout-payment-details.js +114 -10
  77. package/src/js/page/components/gift-cards.js +524 -115
  78. package/src/js/page/components/postcode-lookup/postcode-lookup-api.js +56 -0
  79. package/src/js/page/components/postcode-lookup/postcode-lookup-render.js +98 -0
  80. package/src/js/page/components/postcode-lookup/postcode-lookup-validation.js +81 -0
  81. package/src/js/page/components/postcode-lookup.js +503 -0
  82. package/src/js/page/ripple/bulk-add.js +2 -2
  83. package/src/js/page/ripple/thumb-gallery.js +14 -8
  84. package/src/js/page/utils/address-utils.js +32 -0
  85. package/src/js/page/utils/validation.js +54 -1
  86. package/src/page_checkout_confirmation-gift-card-split-payment.html +339 -0
  87. package/src/page_checkout_confirmation-gift-card.html +339 -0
  88. package/src/page_my_account_payment_info_gift_cards.html +96 -0
  89. package/src/page_my_account_payment_info_gift_cards_split_payment.html +96 -0
  90. package/src/page_product-details-billie.html +2 -0
  91. package/src/page_product-details-clearpay.html +1 -0
  92. package/src/scss/common/_CTAs.scss +4 -0
  93. package/src/scss/components/_gift-cards.scss +66 -9
  94. package/src/scss/components/_payments-checkout.scss +2 -2
  95. package/src/scss/components/_payments-v2.scss +5 -7
  96. package/src/scss/components/_postcode-lookup-v2.scss +164 -0
  97. package/src/scss/components/checkout-payment-details-v2.scss +1 -0
  98. package/src/scss/helpers/_CTAs.scss +26 -0
  99. package/src/scss/helpers/_variables.scss +2 -0
  100. package/src/scss/pages/_checkout-confirmation-new.scss +96 -3
  101. package/src/scss/pages/page_wismo-details.scss +29 -0
  102. package/src/sitemap.html +24 -0
@@ -1,6 +1,7 @@
1
1
  import { initializeInputToggle } from '../utils/show-hide-input';
2
2
  import { updateOrderSummary } from './order-summary';
3
- import { hideLoader, showLoader, appendLoader } from '../utils/loader';
3
+ import { createPostcodeLookup } from './postcode-lookup';
4
+ import { hideLoader, showLoader } from '../utils/loader';
4
5
  import {
5
6
  ADD_GIFT_CARD,
6
7
  buildHintContext,
@@ -20,7 +21,18 @@ import {
20
21
  DEFAULT_PAY_ERROR_MESSAGE,
21
22
  } from '../utils/gift-cards-utils';
22
23
 
23
- import { clearErrorText, validateExactDigits } from '../utils/validation';
24
+ import {
25
+ bindBlurValidation,
26
+ clearErrorText,
27
+ isAntiXssValid,
28
+ isElementInViewport,
29
+ removeErrorStyling,
30
+ resetBlurValidation,
31
+ validateExactDigits,
32
+ validateFieldByRule,
33
+ } from '../utils/validation';
34
+ import errorMessages from '../utils/errorMesages';
35
+ import { scrollToElement } from '../utils/helpers';
24
36
  import {
25
37
  createGcNumberInputHandler,
26
38
  digitsOnlyKeypress,
@@ -42,6 +54,13 @@ const notificationTpl = require('../../../elements/notifications.hbs');
42
54
 
43
55
  var Wick = window.Wick || {};
44
56
 
57
+ const ORDER_NAME_RULES = {
58
+ orderFirstName: { required: true, validate: isAntiXssValid, message: errorMessages.errorName },
59
+ orderSurname: { required: true, validate: isAntiXssValid, message: errorMessages.errorSurname },
60
+ };
61
+ const ORDER_ADDRESS_SCROLL_OFFSET = 100;
62
+ const ORDER_ADDRESS_LOOKUP_CONTEXT = 'order-address';
63
+
45
64
  Wick.GiftCard = {
46
65
  ADD_GIFT_CARD,
47
66
  REMOVE_GIFT_CARD,
@@ -114,6 +133,19 @@ Wick.GiftCard = {
114
133
  '[data-apple-pay],[data-google-pay],[data-paypal],[data-klarna],[data-billie],[data-clearpay],[data-existing-card]',
115
134
  hiddenCard: 'checkout-payment-details__card-details_hidden',
116
135
  cardDetails: '.checkout-payment-details__card-details',
136
+ orderAddress: '[data-order-address]',
137
+ orderFirstName: '#order-first-name',
138
+ orderSurname: '#order-surname',
139
+ paymentMethodCheckedSel: 'input[name="payment-method"]:checked',
140
+ paymentMethodCardValue: 'card',
141
+ paypageIframeWrapSel: '.hop-iframe__wrapper',
142
+ cardValidationErrorRowSel: '.form-row_validation-error',
143
+ cardValidationErrorRowClass: 'form-row_validation-error',
144
+ cardValidationErrorTextSel: '.form-row__error',
145
+ paypageFormSel: '#hopPostForm',
146
+ paypageIframeSel: '#hopIframe',
147
+ paypageCardDetailsSel: '.checkout-payment-details__card-details',
148
+ wrapper: '.giftcard__wrapper',
117
149
  },
118
150
 
119
151
  messages: {
@@ -123,12 +155,17 @@ Wick.GiftCard = {
123
155
  ctaGiftcardText: 'Pay with Gift Card',
124
156
  },
125
157
 
158
+ collapseTargets: [
159
+ '.billing-address input, .billing-address select, .billing-address textarea',
160
+ '.billing-address [data-finder], .billing-address [data-manually]',
161
+ ].join(','),
162
+
126
163
  maxChips: 3,
127
164
  successTimer: null,
128
- pendingServerGiftCards: null,
129
165
  altPaymentInfosLocked: false,
130
166
  altPaymentPanelsLocked: false,
131
167
  summaryTotal: null,
168
+ orderAddressLookup: null,
132
169
 
133
170
  resetPinToggle($root) {
134
171
  const {
@@ -166,6 +203,90 @@ Wick.GiftCard = {
166
203
  $pinInput.trigger(passwordToggleEvent);
167
204
  },
168
205
 
206
+ isPanelOpen($root) {
207
+ const $panel = $root.find(this.el.panel);
208
+ return $panel.length ? $panel.prop('hidden') === false : false;
209
+ },
210
+
211
+ openPaypage($container) {
212
+ const {
213
+ paypageFormSel,
214
+ paypageIframeSel,
215
+ paypageIframeWrapSel,
216
+ paypageCardDetailsSel,
217
+ hiddenCard,
218
+ } = this.el;
219
+
220
+ const $cardDetails = $container.find(paypageCardDetailsSel);
221
+ const $wrap = $container.find(paypageIframeWrapSel);
222
+
223
+ $cardDetails.removeClass(hiddenCard).show();
224
+ $wrap.show();
225
+
226
+ const $form = $container.find(paypageFormSel);
227
+
228
+ const form = $form.get(0);
229
+ if (!form) return;
230
+
231
+ const $oldIframe = $container.find(paypageIframeSel);
232
+ if ($oldIframe.length) {
233
+ const old = $oldIframe.get(0);
234
+ const newIframe = old.cloneNode(true);
235
+
236
+ newIframe.src = old.getAttribute('src') || newIframe.src || '';
237
+ old.parentNode.replaceChild(newIframe, old);
238
+
239
+ const iframeName = newIframe.getAttribute('name') || newIframe.name;
240
+ if (iframeName) form.setAttribute('target', iframeName);
241
+ }
242
+ },
243
+
244
+ getAppliedChipsCount($root) {
245
+ const { giftcardRow, chipList, chip } = this.el;
246
+ const $row = $root.closest(giftcardRow);
247
+ return $row.length ? $row.find(`${chipList} ${chip}`).length : 0;
248
+ },
249
+
250
+ collapseKeepValues($root) {
251
+ const { toggle, panel, hint, expandedAttr } = this.el;
252
+
253
+ $root.find(panel).prop('hidden', true);
254
+ $root.find(toggle).attr(expandedAttr, 'false').show();
255
+
256
+ const zeroTotal = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
257
+ $root.find(hint)[zeroTotal ? 'hide' : 'show']();
258
+ },
259
+
260
+ handleCollapseTrigger(e) {
261
+ const $target = $(e.target);
262
+
263
+ const isCheckbox = $target.is('#chb-payment-details-card');
264
+ const isInBilling = $target.closest('.billing-address').length > 0;
265
+
266
+ if (!isCheckbox && !isInBilling) return;
267
+
268
+ const $container = $target.closest(this.el.checkoutContainer);
269
+ if (!$container.length) return;
270
+
271
+ const $giftcard = $container.find(this.el.block).first();
272
+ if (!$giftcard.length) return;
273
+
274
+ if (!this.isPanelOpen($giftcard)) return;
275
+
276
+ const chipsCount = this.getAppliedChipsCount($giftcard);
277
+
278
+ const { panel, toggle, expandedAttr } = this.el;
279
+
280
+ if (chipsCount > 0) {
281
+ this.updateHintVisibility($giftcard);
282
+ this.collapseGiftcardPanel($container, { hideIfHasChips: true });
283
+ $giftcard.find(panel).prop('hidden', true).hide();
284
+ $giftcard.find(toggle).attr(expandedAttr, 'false');
285
+ return;
286
+ }
287
+ this.collapseKeepValues($giftcard);
288
+ },
289
+
169
290
  resetForm($root) {
170
291
  const { number, pin, note } = this.el;
171
292
  const $num = $root.find(number);
@@ -195,73 +316,104 @@ Wick.GiftCard = {
195
316
 
196
317
  open($root) {
197
318
  const { toggle, panel, hint, pin, expandedAttr, passwordToggleEvent } = this.el;
198
- this.hideBanner($root);
199
319
  $root.show();
200
320
 
201
- $root.find(panel).prop('hidden', false);
321
+ $root.find(panel).prop('hidden', false).show();
202
322
  $root.find(toggle).attr(expandedAttr, 'true').hide();
203
323
  $root.find(hint).hide();
204
324
  $root.find(pin).trigger(passwordToggleEvent);
205
325
  },
206
326
 
207
327
  close($root) {
208
- const { toggle, panel, loader, expandedAttr } = this.el;
328
+ const { toggle, panel, expandedAttr, giftcardRow, chipList, chip, hint } = this.el;
209
329
  const $toggle = $root.find(toggle);
210
330
  const $panel = $root.find(panel);
211
- const $loader = $root.find(loader);
331
+ const $pageLoader = this.getPageLoader();
212
332
 
213
- showLoader($loader);
333
+ showLoader($pageLoader);
214
334
 
215
335
  setTimeout(() => {
216
- hideLoader($loader);
336
+ hideLoader($pageLoader);
217
337
 
218
338
  $panel.prop('hidden', true);
219
339
  $toggle.attr(expandedAttr, 'false').show().trigger('focus');
340
+
341
+ const $row = $root.closest(giftcardRow);
342
+ const count = $row.find(`${chipList} ${chip}`).length;
343
+ const zeroTotal = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
344
+
345
+ if (count === 0) {
346
+ $root.find(hint)[zeroTotal ? 'hide' : 'show']();
347
+ return;
348
+ }
349
+
220
350
  this.updateHintVisibility($root);
221
351
  }, 1000);
222
352
  },
223
353
 
224
354
  showSubmitSpinner($root) {
225
355
  const { panel, toggle, hint, expandedAttr } = this.el;
356
+
226
357
  $root.find(panel).prop('hidden', true);
227
358
  $root.find(toggle).attr(expandedAttr, 'false').show().trigger('focus');
228
359
  $root.find(hint).show();
360
+
361
+ this.lockPaymentsForSuccess($root);
362
+
229
363
  this.showSuccess($root);
230
364
  },
231
365
 
232
366
  handleToggleClick(e) {
233
367
  e.preventDefault();
234
- this.open($(e.currentTarget).closest(this.el.$blocks));
368
+
369
+ const $root = $(e.currentTarget).closest(this.el.block);
370
+ const $container = $root.closest(this.el.checkoutContainer);
371
+
372
+ if (
373
+ $container.length &&
374
+ this.isCardMethodSelected($container) &&
375
+ this.isPaypageOpen($container)
376
+ ) {
377
+ this.clearPaypageAndCollapse($container);
378
+ }
379
+
380
+ this.open($root);
381
+ this.updateCtaButton($root);
235
382
  },
236
383
 
237
384
  handleCloseClick(e) {
238
385
  e.preventDefault();
239
- const $root = $(e.currentTarget).closest(this.el.$blocks);
386
+ const $root = $(e.currentTarget).closest(this.el.block);
240
387
  this.resetForm($root);
241
388
  this.close($root);
242
389
  },
243
390
 
244
391
  finalizeSuccess($root, $success) {
245
- let { panel, toggle, hint, expandedAttr } = this.el;
246
-
247
392
  if ($success && $success.length) {
248
393
  $success.attr('hidden', 'hidden').removeClass('fade show').off('transitionend');
249
394
  }
250
395
 
251
396
  this.resetForm($root);
252
- $root.find(panel).prop('hidden', true);
253
- $root.find(toggle).attr(expandedAttr, 'false').show();
254
- $root.find(hint).show();
255
-
256
- if (this.pendingServerGiftCards) {
257
- this.renderGiftCardsFromList($root, this.pendingServerGiftCards);
258
- this.pendingServerGiftCards = null;
259
- }
260
-
261
397
  this.updateHintVisibility($root);
398
+
262
399
  this.successTimer = null;
263
400
  },
264
401
 
402
+ lockPaymentsForSuccess($root) {
403
+ const $giftcard = $root.closest(this.el.block);
404
+ const $container = $giftcard.closest(this.el.checkoutContainer);
405
+
406
+ this.toggleAltPayments($giftcard, false);
407
+ this.toggleAltPaymentInfos($giftcard, false);
408
+
409
+ $container.find(this.el.billingAddress).show();
410
+
411
+ $container.find(this.el.altPaymentRowsAttr).closest(this.el.formRow).hide();
412
+
413
+ this.altPaymentPanelsLocked = true;
414
+ this.altPaymentInfosLocked = true;
415
+ },
416
+
265
417
  showSuccess($root) {
266
418
  const { success } = this.el;
267
419
 
@@ -278,15 +430,16 @@ Wick.GiftCard = {
278
430
 
279
431
  this.successTimer = setTimeout(() => {
280
432
  $success.removeClass('show');
281
- const handler = () => this.finalizeSuccess($root, $success);
282
433
 
434
+ const handler = () => this.finalizeSuccess($root, $success);
283
435
  $success.on('transitionend', handler);
436
+
284
437
  setTimeout(handler, 200);
285
- }, 3000);
438
+ }, 1500);
286
439
  },
287
440
 
288
441
  handleFormSubmit(e) {
289
- const { number, block, giftcardRow, loaderChip } = this.el;
442
+ const { number, block } = this.el;
290
443
  e.preventDefault();
291
444
 
292
445
  const $root = $(e.currentTarget).closest(block);
@@ -298,48 +451,50 @@ Wick.GiftCard = {
298
451
  const $numInput = $root.find(this.el.number);
299
452
  const $pinInput = $root.find(this.el.pin);
300
453
 
301
- const numberBlurOpts = {
302
- fieldSelector: this.el.field,
303
- errorTextSelector: this.el.errorText,
304
- errorClass: this.el.fieldError,
305
- ariaInvalidAttr: this.el.ariaInvalidAttr,
306
- numberMessage: this.messages.numberMessage,
307
- };
308
-
309
- const pinBlurOpts = {
310
- fieldSelector: this.el.field,
311
- errorTextSelector: this.el.errorText,
312
- errorClass: this.el.fieldError,
313
- ariaInvalidAttr: this.el.ariaInvalidAttr,
314
- pinMessage: this.messages.pinMessage,
315
- };
316
-
317
454
  const okNumber = validateExactDigits($numInput, 16, this.messages.numberMessage, this.el);
318
455
  const okPin = validateExactDigits($pinInput, 8, this.messages.pinMessage, this.el);
319
456
 
320
457
  if (!(okNumber && okPin)) {
321
- handleGcNumberBlur({ currentTarget: $numInput[0] }, numberBlurOpts);
322
- handleGcPinBlur({ currentTarget: $pinInput[0] }, pinBlurOpts);
458
+ handleGcNumberBlur(
459
+ { currentTarget: $numInput[0] },
460
+ {
461
+ fieldSelector: this.el.field,
462
+ errorTextSelector: this.el.errorText,
463
+ errorClass: this.el.fieldError,
464
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
465
+ numberMessage: this.messages.numberMessage,
466
+ }
467
+ );
468
+
469
+ handleGcPinBlur(
470
+ { currentTarget: $pinInput[0] },
471
+ {
472
+ fieldSelector: this.el.field,
473
+ errorTextSelector: this.el.errorText,
474
+ errorClass: this.el.fieldError,
475
+ ariaInvalidAttr: this.el.ariaInvalidAttr,
476
+ pinMessage: this.messages.pinMessage,
477
+ }
478
+ );
479
+
323
480
  (!okNumber ? $numInput : $pinInput).trigger('focus');
324
481
  return;
325
482
  }
326
483
 
327
484
  const giftCardNumber = this.getNumberValue($root);
328
485
  const pin = this.getPinValue($root);
329
- const $row = $root.closest(giftcardRow);
330
486
 
331
- let $loader = $row.children(loaderChip);
332
-
333
- if (!$loader.length) {
334
- appendLoader({ wrapper: $row });
335
- $loader = $row.children(loaderChip).last();
336
- }
487
+ const $pageLoader = this.getPageLoader();
488
+ showLoader($pageLoader);
337
489
 
338
- verifyGiftCard($loader, giftCardNumber, pin)
490
+ verifyGiftCard($pageLoader, giftCardNumber, pin)
339
491
  .then((res) => this.handleVerifyResponse($root, res))
340
492
  .catch((err) => {
341
493
  const msg = this.getErrorMessage(err);
342
494
  this.showBannerError($root, msg);
495
+ })
496
+ .finally(() => {
497
+ hideLoader($pageLoader);
343
498
  });
344
499
  },
345
500
 
@@ -350,18 +505,20 @@ Wick.GiftCard = {
350
505
  return;
351
506
  }
352
507
 
353
- this.pendingServerGiftCards = res.giftCards.slice();
354
-
508
+ let summaryData;
355
509
  if (res.orderTotal != null || res.giftCardTotal != null) {
356
- const data = { total: res.orderTotal, giftCardApplied: res.giftCardTotal };
357
- updateOrderSummary(data, buildSummaryEqualMap(data), true);
510
+ summaryData = { total: res.orderTotal, giftCardApplied: res.giftCardTotal };
511
+
512
+ updateOrderSummary(summaryData, buildSummaryEqualMap(summaryData), true);
358
513
  this.summaryTotal = res.orderTotal;
514
+
359
515
  this.toggleBillingDescrForZero($root);
360
516
  this.updateCtaButton($root);
361
517
  }
362
518
 
363
519
  this.resetForm($root);
364
520
  this.showSubmitSpinner($root);
521
+ this.renderGiftCardsFromList($root, res.giftCards);
365
522
  return;
366
523
  }
367
524
 
@@ -369,10 +526,9 @@ Wick.GiftCard = {
369
526
  this.showBannerError($root, msg);
370
527
  },
371
528
 
372
- updateHintVisibility($root) {
529
+ updateHintVisibility($root, opts = {}) {
373
530
  const {
374
531
  chipList,
375
- giftcardRow,
376
532
  hint,
377
533
  panel,
378
534
  toggle,
@@ -382,61 +538,63 @@ Wick.GiftCard = {
382
538
  chip,
383
539
  block,
384
540
  body,
541
+ fieldError,
385
542
  } = this.el;
386
543
 
387
- const $row = $root.closest(giftcardRow);
388
- const $giftcard = $row.find(block).first();
389
- const count = $row.find(`${chipList} ${chip}`).length;
390
- const limit = this.maxChips || 3;
544
+ const { skipResetForm = false } = opts;
391
545
 
546
+ const $giftcard = $root.closest(block).length ? $root.closest(block) : $root;
547
+ const $summary = this.ensureSummaryContainer($giftcard);
548
+ const count = $summary.find(`${chipList} ${chip}`).length;
549
+
550
+ const limit = this.maxChips || 3;
392
551
  const zeroTotal = this.summaryTotal != null ? isZeroAmount(this.summaryTotal) : false;
393
552
 
394
553
  this.toggleAltPayments($giftcard, count === 0);
395
554
  this.toggleAltPaymentInfos($giftcard, count === 0);
555
+ const panelOpen = this.isPanelOpen($giftcard);
396
556
 
397
- if (count > 0) {
398
- if (!this.altPaymentPanelsLocked) {
399
- this.showOnlyBillingAddress($giftcard);
400
- this.altPaymentPanelsLocked = true;
401
- } else {
402
- this.showOnlyBillingAddress($giftcard);
557
+ if (count === 0) {
558
+ $summary.hide();
559
+
560
+ $giftcard.show();
561
+
562
+ if (!skipResetForm) {
563
+ this.resetForm($giftcard);
403
564
  }
404
- } else {
405
- if (!this.altPaymentPanelsLocked) {
406
- this.toggleAltPayments($giftcard, true);
407
- this.toggleAltPaymentInfos($giftcard, true);
565
+
566
+ if (panelOpen) {
567
+ this.open($giftcard);
408
568
  } else {
409
- this.showOnlyBillingAddress($giftcard);
569
+ $giftcard.find(panel).prop('hidden', true);
570
+ $giftcard.find(toggle).attr(expandedAttr, 'false').show();
571
+ $giftcard.find(hint)[zeroTotal ? 'hide' : 'show']();
410
572
  }
411
- }
412
-
413
- const $summary = this.ensureSummaryContainer($giftcard);
414
573
 
415
- if (count === 0) {
416
- $summary.hide();
417
- $giftcard.show();
418
- this.resetForm($giftcard);
419
- $giftcard.find(panel).prop('hidden', true);
420
- $giftcard.find(toggle).attr(expandedAttr, 'false').show();
421
- $giftcard.find(hint)[zeroTotal ? 'hide' : 'show']();
422
574
  this.updateCtaButton($giftcard);
423
575
  return;
424
576
  }
425
577
 
426
578
  this.setBillingLabelToCard($giftcard);
579
+
427
580
  $summary.show();
428
- $giftcard.hide();
429
581
  $giftcard.find(hint).hide();
582
+ const hasError = $giftcard.find(`.${fieldError}`).length > 0;
583
+
584
+ const shouldHideGiftcard = count > 0 && !panelOpen && !hasError;
585
+
586
+ $giftcard.toggle(!shouldHideGiftcard);
430
587
 
431
588
  const ctx = buildHintContext(count, limit, zeroTotal);
432
589
  const html = giftCardsHint(ctx);
590
+
433
591
  const $old = $summary.find(summaryHint).first();
434
592
  const $new = $(html);
435
593
 
436
594
  if ($old.length) $old.replaceWith($new);
437
595
  else $summary.append($new);
438
596
 
439
- if (zeroTotal) {
597
+ if (zeroTotal || panelOpen) {
440
598
  $summary.find(summaryHint).hide();
441
599
  }
442
600
 
@@ -452,18 +610,21 @@ Wick.GiftCard = {
452
610
  },
453
611
 
454
612
  toggleAltPaymentInfos($root, show) {
455
- const { checkoutContainer, billieInfo, klarnaInfo } = this.el;
613
+ const { checkoutContainer, billieInfo, klarnaInfo, paymentMethodCheckedSel } = this.el;
614
+
456
615
  const $container = $root.closest(checkoutContainer);
457
616
 
458
617
  if (this.altPaymentInfosLocked) {
459
618
  show = false;
460
619
  }
461
620
 
462
- [$container.find(billieInfo), $container.find(klarnaInfo)].forEach(($panel) => {
463
- if ($panel && $panel.length) {
464
- $panel[show ? 'show' : 'hide']();
465
- }
466
- });
621
+ const currentMethod = $container.find(paymentMethodCheckedSel).val();
622
+
623
+ const showBillie = show && currentMethod === 'billie';
624
+ const showKlarna = show && currentMethod === 'klarna';
625
+
626
+ $container.find(billieInfo)[showBillie ? 'show' : 'hide']();
627
+ $container.find(klarnaInfo)[showKlarna ? 'show' : 'hide']();
467
628
 
468
629
  if (!show) this.altPaymentInfosLocked = true;
469
630
  },
@@ -532,16 +693,48 @@ Wick.GiftCard = {
532
693
 
533
694
  handleAddAnotherClick(e) {
534
695
  e.preventDefault();
535
- const { giftcardRow, chipList, chip, block, number, summaryHint } = this.el;
696
+
697
+ const {
698
+ giftcardRow,
699
+ chipList,
700
+ chip,
701
+ block,
702
+ number,
703
+ pin,
704
+ summaryHint,
705
+ checkoutContainer,
706
+ fieldError,
707
+ notificationRoot,
708
+ } = this.el;
536
709
 
537
710
  const $row = $(e.currentTarget).closest(giftcardRow);
711
+
712
+ const $container = $row.closest(checkoutContainer);
713
+ if (
714
+ $container.length &&
715
+ this.isCardMethodSelected($container) &&
716
+ this.isPaypageOpen($container)
717
+ ) {
718
+ this.clearPaypageAndCollapse($container);
719
+ }
720
+
538
721
  const count = $row.find(`${chipList} ${chip}`).length;
539
722
  if (count >= this.maxChips) return;
540
723
 
541
724
  const $giftcard = $row.find(block).first();
542
- this.resetForm($giftcard);
543
- this.open($giftcard);
544
725
 
726
+ const hasDraft =
727
+ String($giftcard.find(number).val() || '').trim().length > 0 ||
728
+ String($giftcard.find(pin).val() || '').trim().length > 0;
729
+
730
+ const hasFieldError = $giftcard.find(`.${fieldError}`).length > 0;
731
+ const hasBannerError = $giftcard.find(notificationRoot).length > 0;
732
+
733
+ if (!hasDraft && !hasFieldError && !hasBannerError) {
734
+ this.resetForm($giftcard);
735
+ }
736
+
737
+ this.open($giftcard);
545
738
  $row.find(summaryHint).hide();
546
739
 
547
740
  setTimeout(() => {
@@ -549,6 +742,43 @@ Wick.GiftCard = {
549
742
  }, 0);
550
743
  },
551
744
 
745
+ isCardMethodSelected($container) {
746
+ const { paymentMethodCheckedSel, paymentMethodCardValue } = this.el;
747
+
748
+ const $checked = $container.find(paymentMethodCheckedSel);
749
+ return $checked.length && $checked.val() === paymentMethodCardValue;
750
+ },
751
+
752
+ isPaypageOpen($container) {
753
+ const { paypageIframeWrapSel } = this.el;
754
+
755
+ const $wrap = $container.find(paypageIframeWrapSel);
756
+ return $wrap.length ? $wrap.is(':visible') : false;
757
+ },
758
+
759
+ clearPaypageAndCollapse($container) {
760
+ const {
761
+ cardDetails,
762
+ hiddenCard,
763
+ paypageIframeWrapSel,
764
+ cardValidationErrorRowSel,
765
+ cardValidationErrorRowClass,
766
+ cardValidationErrorTextSel,
767
+ } = this.el;
768
+
769
+ const $cardDetails = $container.find(cardDetails);
770
+ if (!$cardDetails.length) return;
771
+
772
+ $cardDetails.addClass(hiddenCard).hide();
773
+ $cardDetails.find(paypageIframeWrapSel).hide();
774
+
775
+ $cardDetails
776
+ .find(cardValidationErrorRowSel)
777
+ .removeClass(cardValidationErrorRowClass)
778
+ .find(cardValidationErrorTextSel)
779
+ .remove();
780
+ },
781
+
552
782
  getErrorMessage(payload) {
553
783
  const code =
554
784
  payload && (typeof payload.code === 'number' || typeof payload.code === 'string')
@@ -560,7 +790,8 @@ Wick.GiftCard = {
560
790
 
561
791
  handleChipCloseClick(e) {
562
792
  e.preventDefault();
563
- const { chip, giftcardRow, block, giftcardChipClose, chipList, loaderChip } = this.el;
793
+
794
+ const { chip, giftcardRow, block, giftcardChipClose, chipList } = this.el;
564
795
 
565
796
  const $clickedChip = $(e.currentTarget).closest(chip);
566
797
  const $row = $clickedChip.closest(giftcardRow);
@@ -571,22 +802,19 @@ Wick.GiftCard = {
571
802
  const index = $chips.index($clickedChip);
572
803
  if (index < 0) return;
573
804
 
574
- let $loader = $row.children(loaderChip);
575
- if (!$loader.length) {
576
- appendLoader({ wrapper: $row });
577
- $loader = $row.children(loaderChip).last();
578
- }
579
-
580
805
  $row.find(giftcardChipClose).css('pointer-events', 'none').attr('aria-disabled', 'true');
581
806
 
582
- removeGiftCard($loader, index)
807
+ const $pageLoader = this.getPageLoader();
808
+ showLoader($pageLoader);
809
+
810
+ removeGiftCard($pageLoader, index)
583
811
  .then((res) => this.handleRemoveResponse($giftcard, res))
584
812
  .catch((err) => {
585
813
  const msg = this.getErrorMessage(err);
586
814
  this.showBannerError($giftcard, msg);
587
815
  })
588
816
  .finally(() => {
589
- hideLoader($loader);
817
+ hideLoader($pageLoader);
590
818
  $row.find(giftcardChipClose).css('pointer-events', '').removeAttr('aria-disabled');
591
819
  });
592
820
  },
@@ -597,7 +825,10 @@ Wick.GiftCard = {
597
825
  const data = { total: res.orderTotal, giftCardApplied: res.giftCardTotal };
598
826
  this.summaryTotal = res.orderTotal;
599
827
 
600
- this.renderGiftCardsFromList($giftcard, giftCardsList, data);
828
+ this.renderGiftCardsFromList($giftcard, giftCardsList, {
829
+ ...data,
830
+ skipResetForm: true,
831
+ });
601
832
 
602
833
  if (res.orderTotal != null || res.giftCardTotal != null) {
603
834
  updateOrderSummary(data, buildSummaryEqualMap(data), true);
@@ -625,14 +856,14 @@ Wick.GiftCard = {
625
856
  },
626
857
 
627
858
  getPageLoader() {
628
- const $pl = $(this.el.pageLoader);
629
- if (!$pl.parent().is('body')) $pl.appendTo('body');
630
- return $pl;
859
+ const $pageLoader = $(this.el.pageLoader);
860
+ if (!$pageLoader.parent().is('body')) $pageLoader.appendTo('body');
861
+ return $pageLoader;
631
862
  },
632
863
 
633
864
  showPageLoader() {
634
- const $pl = this.getPageLoader();
635
- showLoader($pl);
865
+ const $pageLoader = this.getPageLoader();
866
+ showLoader($pageLoader);
636
867
  },
637
868
 
638
869
  updateCtaButton($scope) {
@@ -657,13 +888,67 @@ Wick.GiftCard = {
657
888
  }
658
889
  },
659
890
 
891
+ toggleOrderAddress($scope, show) {
892
+ const $container = $scope.closest(this.el.checkoutContainer);
893
+ const $orderAddress = $container.find(this.el.orderAddress);
894
+ if (!$orderAddress.length) return;
895
+
896
+ const wasShown = !$orderAddress.hasClass('d-none');
897
+ if (wasShown === show) return;
898
+
899
+ $orderAddress.toggleClass('d-none', !show);
900
+
901
+ if (this.orderAddressLookup) this.orderAddressLookup.setShown(show);
902
+ },
903
+
904
+ validateOrderNames() {
905
+ let $firstInvalid = null;
906
+
907
+ Object.keys(ORDER_NAME_RULES).forEach((key) => {
908
+ const $field = $(this.el[key]);
909
+ const isValid = validateFieldByRule($field, ORDER_NAME_RULES[key]);
910
+
911
+ if (!isValid && !$firstInvalid) $firstInvalid = $field;
912
+ });
913
+
914
+ return $firstInvalid;
915
+ },
916
+
917
+ isOrderAddressValid() {
918
+ const $invalidName = this.validateOrderNames();
919
+ const isLookupValid =
920
+ !this.orderAddressLookup ||
921
+ this.orderAddressLookup.validate({ shouldScroll: !$invalidName });
922
+
923
+ if ($invalidName) {
924
+ const $row = $invalidName.closest(this.el.formRow);
925
+ if (!isElementInViewport($row)) scrollToElement($row, ORDER_ADDRESS_SCROLL_OFFSET);
926
+ }
927
+
928
+ return !$invalidName && isLookupValid;
929
+ },
930
+
931
+ resetOrderAddress() {
932
+ Object.keys(ORDER_NAME_RULES).forEach((key) => {
933
+ const $field = $(this.el[key]);
934
+ $field.val('');
935
+ removeErrorStyling($field.closest(this.el.formRow));
936
+ resetBlurValidation($field);
937
+ });
938
+
939
+ if (this.orderAddressLookup) this.orderAddressLookup.reset();
940
+ },
941
+
660
942
  toggleBillingDescrForZero($scope) {
661
943
  const $container = $scope.closest(this.el.checkoutContainer);
662
944
  const $descr = $container.find(this.el.checkoutPaymentFields);
945
+ const isFullyCovered = isZeroAmount(this.summaryTotal);
946
+
947
+ this.toggleOrderAddress($scope, isFullyCovered);
948
+
663
949
  if (!$descr.length) return;
664
950
 
665
- const shouldHide = isZeroAmount(this.summaryTotal);
666
- $descr[shouldHide ? 'hide' : 'show']();
951
+ $descr[isFullyCovered ? 'hide' : 'show']();
667
952
  },
668
953
 
669
954
  getNumberValue($root) {
@@ -690,6 +975,8 @@ Wick.GiftCard = {
690
975
  e.stopPropagation();
691
976
  e.stopImmediatePropagation();
692
977
 
978
+ if (!self.isOrderAddressValid()) return;
979
+
693
980
  const $scope = $btn.closest(self.el.checkoutContainer).find(self.el.block).first();
694
981
  const $root = $scope.length ? $scope : $(self.el.block).first();
695
982
 
@@ -719,8 +1006,8 @@ Wick.GiftCard = {
719
1006
  }
720
1007
  })
721
1008
  .finally(() => {
722
- const $pl = self.getPageLoader();
723
- hideLoader($pl);
1009
+ const $pageLoader = self.getPageLoader();
1010
+ hideLoader($pageLoader);
724
1011
  });
725
1012
  });
726
1013
  },
@@ -742,6 +1029,32 @@ Wick.GiftCard = {
742
1029
  $inline.find(inlineHeader).after(html);
743
1030
  },
744
1031
 
1032
+ clearErrorsOnly($root) {
1033
+ const { number, pin, note, field, errorText, fieldError, ariaInvalidAttr } = this.el;
1034
+
1035
+ const $num = $root.find(number);
1036
+ const $pin = $root.find(pin);
1037
+
1038
+ clearErrorText($num, field, errorText, fieldError, ariaInvalidAttr);
1039
+ clearErrorText($pin, field, errorText, fieldError, ariaInvalidAttr);
1040
+
1041
+ $root.find(note).remove();
1042
+
1043
+ this.hideBanner($root);
1044
+ },
1045
+
1046
+ clearErrorsIfEmptyOnInit($root) {
1047
+ const rawNum = String($root.find(this.el.number).val() || '');
1048
+ const rawPin = String($root.find(this.el.pin).val() || '');
1049
+
1050
+ const digitsNum = rawNum.replace(/\D/g, '');
1051
+ const digitsPin = rawPin.replace(/\D/g, '');
1052
+
1053
+ if (digitsNum || digitsPin) return;
1054
+
1055
+ this.clearErrorsOnly($root);
1056
+ },
1057
+
745
1058
  hideBanner($root) {
746
1059
  const { notification } = this.el;
747
1060
  $root.find(notification).remove();
@@ -767,7 +1080,7 @@ Wick.GiftCard = {
767
1080
  $list.children(`${chip}:not(${loaderChip})`).remove();
768
1081
 
769
1082
  const html = (giftCards || [])
770
- .map((g) => this.renderChipHTML(this.mapGiftToChipData($root, g)))
1083
+ .map((giftCard) => this.renderChipHTML(this.mapGiftToChipData($root, giftCard)))
771
1084
  .join('');
772
1085
 
773
1086
  if (html) {
@@ -805,9 +1118,22 @@ Wick.GiftCard = {
805
1118
 
806
1119
  showOnlyBillingAddress($root) {
807
1120
  const { checkoutContainer, billingAddress, altPaymentRowsAttr, hiddenCard } = this.el;
1121
+
808
1122
  const $container = $root.closest(checkoutContainer);
809
- $container.find(billingAddress).removeClass(hiddenCard);
1123
+ if (!$container.length) {
1124
+ return;
1125
+ }
1126
+
1127
+ $container.find(billingAddress).removeClass(hiddenCard).show();
1128
+
810
1129
  $container.find(altPaymentRowsAttr).addClass(hiddenCard);
1130
+
1131
+ if (this.isCardMethodSelected($container)) {
1132
+ this.clearPaypageAndCollapse($container);
1133
+ } else {
1134
+ $container.find(this.el.cardDetails).addClass(hiddenCard);
1135
+ }
1136
+
811
1137
  this.toggleAltPaymentInfos($root, false);
812
1138
  },
813
1139
 
@@ -841,12 +1167,77 @@ Wick.GiftCard = {
841
1167
  });
842
1168
  },
843
1169
 
1170
+ collapseGiftcardPanel($container, opts = {}) {
1171
+ const { hideIfHasChips = false } = opts;
1172
+ const { panel, toggle, expandedAttr, checkoutContainer, block } = this.el;
1173
+
1174
+ const $ctx = $container && $container.length ? $container : $(checkoutContainer).first();
1175
+ const $giftcard = $ctx.find(block).first();
1176
+ if (!$giftcard.length) return;
1177
+
1178
+ $giftcard.find(panel).prop('hidden', true).hide();
1179
+ $giftcard.find(toggle).attr(expandedAttr, 'false');
1180
+
1181
+ this.updateHintVisibility($giftcard, { skipResetForm: true });
1182
+
1183
+ if (hideIfHasChips) {
1184
+ const hasChips = this.getAppliedChipsCount($giftcard) > 0;
1185
+ if (hasChips) {
1186
+ $giftcard.hide();
1187
+ $giftcard.find(toggle).hide();
1188
+ } else {
1189
+ $giftcard.show();
1190
+ $giftcard.find(toggle).show();
1191
+ }
1192
+ } else {
1193
+ $giftcard.find(toggle).show();
1194
+ }
1195
+ },
1196
+
1197
+ bindCtaCardCollapse() {
1198
+ const sel = this.el.btnEnterDetails;
1199
+
1200
+ $(document).off('click.giftcardCardCollapse', sel);
1201
+
1202
+ $(document).on('click.giftcardCardCollapse', sel, (e) => {
1203
+ const $btn = $(e.currentTarget);
1204
+ const mode = $btn.attr('data-mode');
1205
+ if (mode !== 'card') return;
1206
+
1207
+ const $container = $btn.closest(this.el.checkoutContainer);
1208
+
1209
+ console.log('[GC] CTA click', {
1210
+ mode,
1211
+ btnFound: $(e.currentTarget).length,
1212
+ container: $btn.closest(this.el.checkoutContainer).length,
1213
+ });
1214
+
1215
+ if (!$container.length) return;
1216
+
1217
+ e.preventDefault();
1218
+
1219
+ this.collapseGiftcardPanel($container, { hideIfHasChips: true });
1220
+
1221
+ this.openPaypage($container);
1222
+ });
1223
+ },
1224
+
844
1225
  bindEvents() {
845
1226
  const { number, pin, toggle, closeBtn, giftcardBtn, jsGiftcardAdd, giftcardChipClose } =
846
1227
  this.el;
847
1228
 
848
1229
  $(document).off('.giftcard');
849
1230
 
1231
+ $(document).off('focusin.giftcard', this.collapseTargets);
1232
+ $(document).on('focusin.giftcard', this.collapseTargets, (e) =>
1233
+ this.handleCollapseTrigger(e)
1234
+ );
1235
+
1236
+ $(document).off('change.giftcard', '#chb-payment-details-card');
1237
+ $(document).on('change.giftcard', '#chb-payment-details-card', (e) =>
1238
+ this.handleCollapseTrigger(e)
1239
+ );
1240
+
850
1241
  const handleNumberInput = createGcNumberInputHandler({
851
1242
  fieldSelector: this.el.field,
852
1243
  fieldErrorClass: this.el.fieldError,
@@ -898,10 +1289,25 @@ Wick.GiftCard = {
898
1289
  .on('click.giftcard', giftcardChipClose, this.handleChipCloseClick.bind(this));
899
1290
 
900
1291
  this.bindGiftcardCtaHandler();
1292
+ this.bindCtaCardCollapse();
1293
+ this.bindOrderNameValidation();
1294
+ },
1295
+
1296
+ bindOrderNameValidation() {
1297
+ Object.keys(ORDER_NAME_RULES).forEach((key) => {
1298
+ bindBlurValidation(
1299
+ this.el[key],
1300
+ (field) => validateFieldByRule($(field), ORDER_NAME_RULES[key]),
1301
+ { namespace: '.giftcard' }
1302
+ );
1303
+ });
901
1304
  },
902
1305
 
903
1306
  init(payload = {}) {
904
1307
  const { field, pin, $blocks } = this.el;
1308
+ if (!this.orderAddressLookup) {
1309
+ this.orderAddressLookup = createPostcodeLookup(ORDER_ADDRESS_LOOKUP_CONTEXT);
1310
+ }
905
1311
  this.bindEvents();
906
1312
  this.bindSplitPaymentFailedListener();
907
1313
  if ($blocks && $blocks.length) {
@@ -918,7 +1324,10 @@ Wick.GiftCard = {
918
1324
  }
919
1325
 
920
1326
  $blocks.each((index, block) => {
921
- const $pinField = $(block).find(pin).closest(field);
1327
+ const $block = $(block);
1328
+ this.clearErrorsIfEmptyOnInit($block);
1329
+
1330
+ const $pinField = $block.find(pin).closest(field);
922
1331
  if ($pinField.length) {
923
1332
  initializeInputToggle($pinField);
924
1333
  }