wickes-css2 2.106.0-develop.1 → 2.106.0-develop.11
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.
- package/Readme.md +2 -3
- package/build/css/category-main.css +1 -1
- package/build/css/components/checkout-payment-details-v2.css +1 -1
- package/build/css/components/global-search.css +1 -1
- package/build/css/homepage-main.css +1 -1
- package/build/css/kitchen-plp-main.css +1 -1
- package/build/css/main.css +1 -1
- package/build/css/my-account-main-v2.css +1 -1
- package/build/css/my-account-main.css +1 -1
- package/build/css/pages/page_checkout_delivery-new.css +1 -1
- package/build/css/pages/page_products-list-combined.css +1 -1
- package/build/css/pdp-main-before-combine.css +1 -1
- package/build/css/pdp-main-critical.css +1 -1
- package/build/css/pdp-main-non-critical.css +1 -1
- package/build/css/pdp-main.css +1 -1
- package/build/css/plp-main.css +1 -1
- package/build/css/store-locator-main.css +1 -1
- package/build/img/billie-pay-later.svg +13 -0
- package/build/img/giftcard.svg +28 -0
- package/build/js/account-members.min.js +1 -1
- package/build/js/add-project-list-id.min.js +1 -1
- package/build/js/address-book.min.js +1 -1
- package/build/js/basket.min.js +2 -2
- package/build/js/bundle.min.js +1 -1
- package/build/js/change-password.min.js +1 -1
- package/build/js/checkout.min.js +2 -2
- package/build/js/emulation.min.js +2175 -1857
- package/build/js/general.bundle.min.js +1 -1
- package/build/js/gift-cards.min.js +1 -0
- package/build/js/kitchen/kitchen-plp.min.js +1 -1
- package/build/js/merged-checkout.min.js +2 -2
- package/build/js/mini-basket-slider.min.js +1 -1
- package/build/js/page/basket/basket-update-cart-action.js +4 -1
- package/build/js/page/basket/klarna.js +13 -3
- package/build/js/page/basket-v2.js +11 -7
- package/build/js/page/components/billie-modal.js +20 -20
- package/build/js/page/components/gift-cards.js +927 -0
- package/build/js/page/components/order-summary.js +57 -28
- package/build/js/page/components/toggle-password-visibility.js +22 -0
- package/build/js/page/components/voucher.js +52 -47
- package/build/js/page/count-stepper/pdp-count-stepper.js +29 -0
- package/build/js/page/filters/plp-filters-mobile.js +4 -7
- package/build/js/page/filters/plp-filters-utils.js +9 -0
- package/build/js/page/filters/plp-filters.js +13 -5
- package/build/js/page/kitchen/card-carousel.js +3 -2
- package/build/js/page/kitchen/colour-swatches.js +157 -152
- package/build/js/page/quick-filter.js +106 -103
- package/build/js/page/share-project-list.js +151 -33
- package/build/js/page/utils/create-count-stepper.js +141 -0
- package/build/js/page/utils/gift-cards-utils.js +242 -0
- package/build/js/page/utils/input-handling.js +104 -0
- package/build/js/page/utils/show-hide-input.js +31 -0
- package/build/js/page/utils/validation.js +146 -96
- package/build/js/pdp-count-stepper.min.js +1 -0
- package/build/js/pdp.bundle.min.js +1 -1
- package/build/js/personal-details.min.js +1 -1
- package/build/js/plp-filters.min.js +2 -2
- package/build/js/plp.bundle.min.js +1 -1
- package/build/js/project-list.min.js +57 -14
- package/build/js/quiz.min.js +1 -1
- package/build/js/share-project-list.min.js +1 -1
- package/build/js/track-my-order.min.js +1 -1
- package/package.json +3 -3
- package/src/components/card_product_v2.hbs +6 -1
- package/src/components/checkout-payment-details-v2.hbs +4 -3
- package/src/components/gift-cards-hint.hbs +9 -0
- package/src/components/gift-cards.hbs +90 -0
- package/src/components/giftcard-chip.hbs +23 -0
- package/src/components/giftcard-summary.hbs +6 -0
- package/src/components/home-movers/form.hbs +1 -1
- package/src/components/kitchen/slide.hbs +8 -6
- package/src/components/price-block-v2.hbs +17 -16
- package/src/data/data_search-results_v2.json +2 -1
- package/src/elements/count-button.hbs +10 -0
- package/src/elements/form-row.hbs +1 -1
- package/src/elements/input.hbs +31 -2
- package/src/img/billie-pay-later.svg +13 -0
- package/src/img/giftcard.svg +28 -0
- package/src/js/components/general/notification.js +60 -40
- package/src/js/components/general/switchVat.js +151 -154
- package/src/js/components/pdp-billie-modal-scroll-reset.js +46 -42
- package/src/js/emulation/basket-data.js +2126 -2038
- package/src/js/emulation/billie-modal.js +6 -6
- package/src/js/emulation/checkLocalEnv.js +6 -5
- package/src/js/emulation/checkout-data.js +33 -0
- package/src/js/emulation/checkout-payment-details.js +23 -21
- package/src/js/emulation/forms.js +9 -2
- package/src/js/emulation/gift-cards.js +216 -0
- package/src/js/emulation/mock.js +98 -21
- package/src/js/emulation/plp-load-more.js +19 -13
- package/src/js/emulation/validity-emulation.js +33 -0
- package/src/js/page/basket/basket-update-cart-action.js +4 -1
- package/src/js/page/basket/klarna.js +13 -3
- package/src/js/page/basket-v2.js +11 -7
- package/src/js/page/components/billie-modal.js +20 -20
- package/src/js/page/components/gift-cards.js +927 -0
- package/src/js/page/components/order-summary.js +57 -28
- package/src/js/page/components/toggle-password-visibility.js +22 -0
- package/src/js/page/components/voucher.js +52 -47
- package/src/js/page/count-stepper/pdp-count-stepper.js +29 -0
- package/src/js/page/filters/plp-filters-mobile.js +4 -7
- package/src/js/page/filters/plp-filters-utils.js +9 -0
- package/src/js/page/filters/plp-filters.js +13 -5
- package/src/js/page/kitchen/card-carousel.js +3 -2
- package/src/js/page/kitchen/colour-swatches.js +157 -152
- package/src/js/page/quick-filter.js +106 -103
- package/src/js/page/share-project-list.js +151 -33
- package/src/js/page/utils/create-count-stepper.js +141 -0
- package/src/js/page/utils/gift-cards-utils.js +242 -0
- package/src/js/page/utils/input-handling.js +104 -0
- package/src/js/page/utils/show-hide-input.js +31 -0
- package/src/js/page/utils/validation.js +146 -96
- package/src/layouts/checkout.hbs +1 -5
- package/src/page_my-account_change-password.html +1 -0
- package/src/page_payment-details-with-gift-card.html +8 -5
- package/src/page_plp-with-paint.html +1 -1
- package/src/page_plp_v2-energy-efficiency.html +1 -1
- package/src/page_plp_v2-favourite-products.html +1 -1
- package/src/page_plp_v2-vat.html +31 -26
- package/src/page_plp_v2.html +1 -1
- package/src/page_product-details-cnc-success-notification-vat.html +3 -2
- package/src/page_product_search-vat.html +1 -1
- package/src/page_product_search.html +1 -1
- package/src/page_search-results.html +1 -1
- package/src/partials/scripts.hbs +4 -0
- package/src/scss/components/_custom-slider.scss +7 -6
- package/src/scss/components/_gift-cards.scss +359 -0
- package/src/scss/components/_input-counter.scss +65 -0
- package/src/scss/components/_quick-filter.scss +32 -10
- package/src/scss/components/_search-filter.scss +3 -0
- package/src/scss/components/checkout-payment-details-v2.scss +6 -2
- package/src/scss/components/kitchen/_price-tooltip.scss +3 -2
- package/src/scss/components/kitchen/_price.scss +2 -2
- package/src/scss/helpers/_variables.scss +7 -16
- package/src/scss/pages/page_checkout_delivery-new.scss +30 -6
- package/src/scss/pages/page_products-list-combined.scss +34 -2
- package/src/scss/pdp-main.scss +1 -0
- package/build/js/page/plp-img-v2.js +0 -25
- package/src/components/.DS_Store +0 -0
- package/src/img/.DS_Store +0 -0
- package/src/img/placeholders/.DS_Store +0 -0
- package/src/js/.DS_Store +0 -0
- package/src/js/components/share-project-list.js +0 -56
- package/src/js/components/toggle-password-visibility.js +0 -58
- package/src/js/page/.DS_Store +0 -0
- package/src/js/page/plp-img-v2.js +0 -25
- package/src/scss/.DS_Store +0 -0
- package/src/scss/pages/.DS_Store +0 -0
|
@@ -1,57 +1,175 @@
|
|
|
1
1
|
import { copyToClipboard } from './utils/copy-to-clipboard';
|
|
2
2
|
|
|
3
|
-
var Wick = Wick || {};
|
|
3
|
+
var Wick = window.Wick || {};
|
|
4
|
+
|
|
4
5
|
Wick.ShareProjectList = (function () {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
const SELECTORS = {
|
|
7
|
+
input: '#share-list-modal #shopping-list-link',
|
|
8
|
+
linkContainer: '#copy-link',
|
|
9
|
+
link: 'a',
|
|
10
|
+
};
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const CLASSES = {
|
|
13
|
+
copied: 'copy-link_hovered',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function logActiveElement() {
|
|
17
|
+
const active = document.activeElement;
|
|
18
|
+
|
|
19
|
+
if (!active) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function getElements() {
|
|
25
|
+
const $input = $(SELECTORS.input);
|
|
26
|
+
const $linkContainer = $(SELECTORS.linkContainer);
|
|
27
|
+
const $link = $linkContainer.find(SELECTORS.link);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
$input: $input,
|
|
31
|
+
$linkContainer: $linkContainer,
|
|
32
|
+
$link: $link,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function enableFocusAndSelect($input, source) {
|
|
37
|
+
if (!$input.length) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
14
40
|
|
|
15
|
-
|
|
16
|
-
|
|
41
|
+
const input = $input.get(0);
|
|
42
|
+
const value = input.value || '';
|
|
43
|
+
const valueLength = value.length;
|
|
17
44
|
|
|
18
|
-
|
|
45
|
+
logActiveElement('[ShareProjectList] before removeAttr disabled');
|
|
19
46
|
|
|
20
|
-
|
|
47
|
+
$input.removeAttr('disabled');
|
|
21
48
|
|
|
22
|
-
|
|
49
|
+
try {
|
|
50
|
+
input.focus();
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.error('[ShareProjectList] input.focus() failed', {
|
|
53
|
+
source: source,
|
|
54
|
+
error: error,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
logActiveElement('[ShareProjectList] after input.focus()');
|
|
23
59
|
|
|
24
|
-
|
|
25
|
-
if (typeof
|
|
26
|
-
|
|
27
|
-
} else if (typeof
|
|
28
|
-
|
|
60
|
+
try {
|
|
61
|
+
if (typeof input.setSelectionRange === 'function') {
|
|
62
|
+
input.setSelectionRange(0, valueLength);
|
|
63
|
+
} else if (typeof input.select === 'function') {
|
|
64
|
+
input.select();
|
|
29
65
|
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.error('[ShareProjectList] selection failed', {
|
|
68
|
+
source: source,
|
|
69
|
+
error: error,
|
|
70
|
+
});
|
|
30
71
|
}
|
|
31
72
|
|
|
32
|
-
|
|
33
|
-
|
|
73
|
+
logActiveElement('[ShareProjectList] after selection');
|
|
74
|
+
|
|
75
|
+
setTimeout(function () {
|
|
76
|
+
logActiveElement('[ShareProjectList] after 0ms timeout');
|
|
77
|
+
}, 0);
|
|
78
|
+
|
|
79
|
+
setTimeout(function () {
|
|
80
|
+
logActiveElement('[ShareProjectList] after 50ms timeout');
|
|
81
|
+
}, 50);
|
|
82
|
+
|
|
83
|
+
setTimeout(function () {
|
|
84
|
+
logActiveElement('[ShareProjectList] after 150ms timeout');
|
|
85
|
+
}, 150);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function getInputValue($input) {
|
|
89
|
+
const value = String($input.val() || '').trim();
|
|
90
|
+
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function bindEvents(elements) {
|
|
95
|
+
const $input = elements.$input;
|
|
96
|
+
const $linkContainer = elements.$linkContainer;
|
|
97
|
+
const $link = elements.$link;
|
|
34
98
|
|
|
35
|
-
|
|
99
|
+
$input.on('focus', function () {
|
|
100
|
+
logActiveElement('[ShareProjectList] on input focus');
|
|
101
|
+
});
|
|
36
102
|
|
|
37
|
-
|
|
38
|
-
|
|
103
|
+
$input.on('blur', function () {
|
|
104
|
+
logActiveElement('[ShareProjectList] on input blur');
|
|
105
|
+
});
|
|
39
106
|
|
|
40
|
-
|
|
107
|
+
$link.on('click', function (event) {
|
|
108
|
+
event.preventDefault();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
$linkContainer.on('mousedown', function () {
|
|
112
|
+
logActiveElement('[ShareProjectList] on mousedown before focus');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
$linkContainer.on('click', function (event) {
|
|
116
|
+
let value;
|
|
117
|
+
|
|
118
|
+
logActiveElement('[ShareProjectList] before click preventDefault');
|
|
119
|
+
|
|
120
|
+
event.preventDefault();
|
|
121
|
+
|
|
122
|
+
enableFocusAndSelect($input, 'linkContainer click');
|
|
123
|
+
|
|
124
|
+
value = getInputValue($input);
|
|
125
|
+
if (!value) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
copyToClipboard(value, $linkContainer.get(0), { noUpperCase: true })
|
|
41
130
|
.then(function () {
|
|
42
|
-
$
|
|
131
|
+
$link.addClass(CLASSES.copied);
|
|
132
|
+
|
|
133
|
+
logActiveElement('[ShareProjectList] after copy success before refocus');
|
|
134
|
+
|
|
135
|
+
enableFocusAndSelect($input, 'copy success refocus');
|
|
136
|
+
|
|
137
|
+
setTimeout(function () {
|
|
138
|
+
enableFocusAndSelect($input, 'copy success refocus timeout 0');
|
|
139
|
+
}, 0);
|
|
140
|
+
|
|
141
|
+
setTimeout(function () {
|
|
142
|
+
enableFocusAndSelect($input, 'copy success refocus timeout 50');
|
|
143
|
+
}, 50);
|
|
43
144
|
})
|
|
44
|
-
.catch(function (
|
|
45
|
-
console.error(
|
|
145
|
+
.catch(function (error) {
|
|
146
|
+
console.error('[ShareProjectList] Failed to copy shopping list link', error);
|
|
147
|
+
|
|
148
|
+
logActiveElement('[ShareProjectList] after copy error before refocus');
|
|
149
|
+
|
|
150
|
+
enableFocusAndSelect($input, 'copy error refocus');
|
|
46
151
|
});
|
|
47
152
|
});
|
|
48
153
|
|
|
49
|
-
$input.on('click', function (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
$actualLink.removeClass(copyClass);
|
|
154
|
+
$input.on('click', function () {
|
|
155
|
+
enableFocusAndSelect($input, 'input click');
|
|
156
|
+
$link.removeClass(CLASSES.copied);
|
|
53
157
|
});
|
|
54
158
|
}
|
|
55
159
|
|
|
56
|
-
init()
|
|
160
|
+
function init() {
|
|
161
|
+
var elements = getElements();
|
|
162
|
+
|
|
163
|
+
if (!elements.$input.length || !elements.$linkContainer.length || !elements.$link.length) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
bindEvents(elements);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
init: init,
|
|
172
|
+
};
|
|
57
173
|
})();
|
|
174
|
+
|
|
175
|
+
Wick.ShareProjectList.init();
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import countButton from '../../../elements/count-button.hbs';
|
|
2
|
+
|
|
3
|
+
// ===== HELPERS =====
|
|
4
|
+
|
|
5
|
+
function normalizeOptions(options) {
|
|
6
|
+
return {
|
|
7
|
+
inputSel: '#quantity-field',
|
|
8
|
+
|
|
9
|
+
min: 1,
|
|
10
|
+
|
|
11
|
+
enhancedInputClass: 'count-stepper__input',
|
|
12
|
+
enhancedParentClass: 'count-stepper',
|
|
13
|
+
|
|
14
|
+
onChange: null,
|
|
15
|
+
|
|
16
|
+
...options,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function toInt(value) {
|
|
21
|
+
const n = parseInt(String(value || '').trim(), 10);
|
|
22
|
+
return Number.isFinite(n) ? n : NaN;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function clampMin(n, min) {
|
|
26
|
+
return Math.max(min, n);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ensureQty(value, fallback) {
|
|
30
|
+
const n = toInt(value);
|
|
31
|
+
return Number.isFinite(n) ? n : fallback;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildBtn(type, disabled) {
|
|
35
|
+
return countButton({
|
|
36
|
+
type,
|
|
37
|
+
icon: type === 'plus' ? 'fa-plus' : 'fa-minus',
|
|
38
|
+
ariaLabel: type === 'plus' ? 'Increase quantity' : 'Decrease quantity',
|
|
39
|
+
disabled: !!disabled,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getButtonsWrap($input) {
|
|
44
|
+
return $input.parent();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getMinusBtn($wrap) {
|
|
48
|
+
return $wrap.find('[data-count-btn][data-count-type="minus"]').first();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getPlusBtn($wrap) {
|
|
52
|
+
return $wrap.find('[data-count-btn][data-count-type="plus"]').first();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function updateButtons($input, opt) {
|
|
56
|
+
const $wrap = getButtonsWrap($input);
|
|
57
|
+
const qty = ensureQty($input.val(), opt.min);
|
|
58
|
+
|
|
59
|
+
const $minus = getMinusBtn($wrap);
|
|
60
|
+
|
|
61
|
+
if ($minus.length) $minus.prop('disabled', qty <= opt.min);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function setQty($input, opt, qty) {
|
|
65
|
+
const next = clampMin(qty, opt.min);
|
|
66
|
+
$input.val(String(next));
|
|
67
|
+
updateButtons($input, opt);
|
|
68
|
+
|
|
69
|
+
if (typeof opt.onChange === 'function') {
|
|
70
|
+
opt.onChange(next, $input.get(0));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function ensureButtons($input, opt) {
|
|
75
|
+
const $parent = getButtonsWrap($input);
|
|
76
|
+
|
|
77
|
+
$input.addClass(opt.enhancedInputClass);
|
|
78
|
+
$parent.addClass(opt.enhancedParentClass);
|
|
79
|
+
|
|
80
|
+
const hasMinus = getMinusBtn($parent).length;
|
|
81
|
+
const hasPlus = getPlusBtn($parent).length;
|
|
82
|
+
|
|
83
|
+
if (!hasMinus) {
|
|
84
|
+
$input.before(buildBtn('minus', true));
|
|
85
|
+
}
|
|
86
|
+
if (!hasPlus) {
|
|
87
|
+
$input.after(buildBtn('plus', false));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const initial = ensureQty($input.val(), opt.min);
|
|
91
|
+
setQty($input, opt, initial);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getInputs($root, inputSel) {
|
|
95
|
+
const $scope = $root && $root.length ? $root : $(document);
|
|
96
|
+
const $self = $scope.is(inputSel) ? $scope : $();
|
|
97
|
+
const $found = $scope.find(inputSel);
|
|
98
|
+
return $self.add($found);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ===== FACTORY =====
|
|
102
|
+
|
|
103
|
+
export function createCountStepper(options = {}) {
|
|
104
|
+
const OPT = normalizeOptions(options);
|
|
105
|
+
if (!OPT.inputSel) throw new Error('[createCountStepper] Missing inputSel');
|
|
106
|
+
|
|
107
|
+
const NS = `.cnt_${Math.random().toString(16).slice(2)}`;
|
|
108
|
+
|
|
109
|
+
function init($root) {
|
|
110
|
+
getInputs($root, OPT.inputSel).each(function () {
|
|
111
|
+
ensureButtons($(this), OPT);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// +/- click
|
|
116
|
+
$(document)
|
|
117
|
+
.off(`click${NS}`, '[data-count-btn]')
|
|
118
|
+
.on(`click${NS}`, '[data-count-btn]', function (e) {
|
|
119
|
+
const $btn = $(this);
|
|
120
|
+
const $parent = $btn.parent();
|
|
121
|
+
const $input = $parent.find(OPT.inputSel).first();
|
|
122
|
+
|
|
123
|
+
if (!$input.length) return;
|
|
124
|
+
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
|
|
127
|
+
const type = ($btn.attr('data-count-type') || '').toLowerCase();
|
|
128
|
+
const current = ensureQty($input.val(), OPT.min);
|
|
129
|
+
|
|
130
|
+
if (type === 'plus') setQty($input, OPT, current + 1);
|
|
131
|
+
if (type === 'minus') setQty($input, OPT, current - 1);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
$(document)
|
|
135
|
+
.off(`input${NS} change${NS}`, OPT.inputSel)
|
|
136
|
+
.on(`input${NS} change${NS}`, OPT.inputSel, function () {
|
|
137
|
+
updateButtons($(this), OPT);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return { init };
|
|
141
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { showLoader, hideLoader } from './loader';
|
|
2
|
+
|
|
3
|
+
const SUMMARY_SELECTORS = {
|
|
4
|
+
subTotal: {
|
|
5
|
+
item: '.checkout-widget__item-bold',
|
|
6
|
+
value: '.checkout-widget__item-value',
|
|
7
|
+
title: 'Items subtotal:',
|
|
8
|
+
},
|
|
9
|
+
vat: {
|
|
10
|
+
item: '.checkout-widget__item-vat',
|
|
11
|
+
value: '.checkout-widget__item-value',
|
|
12
|
+
title: 'VAT:',
|
|
13
|
+
},
|
|
14
|
+
clickAndCollectCost: {
|
|
15
|
+
item: '.checkout-widget__item-cc',
|
|
16
|
+
value: '.checkout-widget__item-value',
|
|
17
|
+
title: 'Click & Collect:',
|
|
18
|
+
},
|
|
19
|
+
deliveryCost: {
|
|
20
|
+
item: '.checkout-widget__item-delivery',
|
|
21
|
+
value: '.checkout-widget__item-value',
|
|
22
|
+
title: 'Delivery:',
|
|
23
|
+
},
|
|
24
|
+
charityPrice: {
|
|
25
|
+
item: '.checkout-widget__item-charity',
|
|
26
|
+
value: '.checkout-widget__item-value',
|
|
27
|
+
title: 'Charity donation:',
|
|
28
|
+
},
|
|
29
|
+
discountTotal: {
|
|
30
|
+
item: '.checkout-widget__details-discount',
|
|
31
|
+
value: '.checkout-widget__detail-value',
|
|
32
|
+
},
|
|
33
|
+
giftCardApplied: {
|
|
34
|
+
item: '.checkout-widget__gift-card',
|
|
35
|
+
value: '.checkout-widget__item-value',
|
|
36
|
+
title: 'Gift Card:',
|
|
37
|
+
},
|
|
38
|
+
total: { item: '.checkout-widget__total', value: '.checkout-widget__total-value' },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const CURRENCY_KEYS = new Set([
|
|
42
|
+
'subTotal',
|
|
43
|
+
'vat',
|
|
44
|
+
'clickAndCollectCost',
|
|
45
|
+
'deliveryCost',
|
|
46
|
+
'charityPrice',
|
|
47
|
+
'discountTotal',
|
|
48
|
+
'total',
|
|
49
|
+
'giftCardApplied',
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
const GIFT_CARD_ALREADY_ADDED = 'GIFT_CARD_ALREADY_ADDED';
|
|
53
|
+
const PIS_REQUEST_TIMEOUT = 'PIS-REQUEST-TIMEOUT';
|
|
54
|
+
const PIS_INTEGRATION_ERROR = 'PIS-INTEGRATION-ERROR';
|
|
55
|
+
const PIS_INSUFFICIENT_AMOUNT = 'PIS-INSUFFICIENT-AMOUNT';
|
|
56
|
+
const PIS_PROCESSING_ERROR = 'PIS_PROCESSING_ERROR';
|
|
57
|
+
const HYBRIS_REQUEST_TIMEOUT = 'HYBRIS-REQUEST-TIMEOUT';
|
|
58
|
+
const HYBRIS_PIS_INTEGRATION_ERROR = 'HYBRIS-PIS_INTEGRATION-ERROR';
|
|
59
|
+
const HYBRIS_PIS_CONNECTION_ERROR = 'HYBRIS-PIS_CONNECTION-ERROR';
|
|
60
|
+
const ZERO_BALANCE = 'ZERO_BALANCE';
|
|
61
|
+
const SPLIT_PAYMENT_CARD_ERROR = 'SPLIT_PAYMENT_CARD_ERROR';
|
|
62
|
+
|
|
63
|
+
export const DEFAULT_ERROR_MESSAGE =
|
|
64
|
+
'Unable to use Gift Card at this time, please try later or click here for support.';
|
|
65
|
+
|
|
66
|
+
export const DEFAULT_PAY_ERROR_MESSAGE =
|
|
67
|
+
'Unfortunately there was a problem placing your order with Gift Card(s). Please click here for support.';
|
|
68
|
+
|
|
69
|
+
export const OK_CODE = 0;
|
|
70
|
+
|
|
71
|
+
export const MESSAGE_BY_CODE = {
|
|
72
|
+
10: 'Gift Card number and / or PIN not recognised. A Gift Card will be locked following three unsuccessful PIN attempts.',
|
|
73
|
+
16: 'Card locked due to three unsuccessful PIN entries. For support click here.',
|
|
74
|
+
20: 'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
75
|
+
21: 'Gift Card number not recognised.',
|
|
76
|
+
40: 'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
77
|
+
60: 'The entered Gift Card has expired. For support click here.',
|
|
78
|
+
75: 'The entered Gift Card is invalid. For support click here.',
|
|
79
|
+
[GIFT_CARD_ALREADY_ADDED]: 'Gift Card already added, please use a different card.',
|
|
80
|
+
[PIS_REQUEST_TIMEOUT]:
|
|
81
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
82
|
+
[PIS_INTEGRATION_ERROR]:
|
|
83
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
84
|
+
[PIS_INSUFFICIENT_AMOUNT]:
|
|
85
|
+
'Unfortunately there was a problem placing your order with Gift Card(s). Please click here for support.',
|
|
86
|
+
[PIS_PROCESSING_ERROR]:
|
|
87
|
+
'Unfortunately there was a problem placing your order with Gift Card(s). Please click here for support.',
|
|
88
|
+
[HYBRIS_REQUEST_TIMEOUT]:
|
|
89
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
90
|
+
[HYBRIS_PIS_INTEGRATION_ERROR]:
|
|
91
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
92
|
+
[HYBRIS_PIS_CONNECTION_ERROR]:
|
|
93
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
94
|
+
[ZERO_BALANCE]:
|
|
95
|
+
'Insufficient funds available, please try another Gift Card or alternate payment method.',
|
|
96
|
+
99: 'The entered Gift Card is invalid. For support click here.',
|
|
97
|
+
[-1]: 'Gift Card is unavailable at this time, please try later. For support click here.',
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const PAY_MESSAGE_BY_CODE = {
|
|
101
|
+
101: 'Unable to use Gift Cards at this time, please try later or click here for support.',
|
|
102
|
+
102: 'Unfortunately there was a problem with requesting your card details from the payment provider. Available funds on your Gift Card are unchanged.',
|
|
103
|
+
103: 'Unfortunately there was a problem placing your order with Gift Card(s). Please click here for support.',
|
|
104
|
+
104: 'Unfortunately there was a problem placing your order with Gift Card(s). Please click here for support.',
|
|
105
|
+
[PIS_REQUEST_TIMEOUT]:
|
|
106
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
107
|
+
[PIS_INTEGRATION_ERROR]:
|
|
108
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
109
|
+
[PIS_INSUFFICIENT_AMOUNT]:
|
|
110
|
+
'Unfortunately there was a problem placing your order with Gift Card(s). Please click here for support.',
|
|
111
|
+
[PIS_PROCESSING_ERROR]:
|
|
112
|
+
'Unfortunately there was a problem placing your order with Gift Card(s). Please click here for support.',
|
|
113
|
+
[HYBRIS_REQUEST_TIMEOUT]:
|
|
114
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
115
|
+
[HYBRIS_PIS_INTEGRATION_ERROR]:
|
|
116
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
117
|
+
[HYBRIS_PIS_CONNECTION_ERROR]:
|
|
118
|
+
'Unable to use Gift Card at this time, please try later or click here for support.',
|
|
119
|
+
[SPLIT_PAYMENT_CARD_ERROR]:
|
|
120
|
+
'Unfortunately there was a problem with requesting your card details from the payment provider. Available funds on your Gift Card are unchanged.',
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const ADD_GIFT_CARD = 'addGiftCard';
|
|
124
|
+
export const REMOVE_GIFT_CARD = 'removeGiftCard';
|
|
125
|
+
export const PAY_WITH_GIFT_CARD = 'payWithGiftCard';
|
|
126
|
+
|
|
127
|
+
export const FAQ_URL = 'https://www.wickes.co.uk/gifts';
|
|
128
|
+
|
|
129
|
+
export const escapeHtml = (s) =>
|
|
130
|
+
$('<div>')
|
|
131
|
+
.text(String(s || ''))
|
|
132
|
+
.html();
|
|
133
|
+
|
|
134
|
+
export const linkifyClickHere = (msg) =>
|
|
135
|
+
escapeHtml(msg).replace(
|
|
136
|
+
/\bclick here\b/gi,
|
|
137
|
+
`<a href="${FAQ_URL}" target="_blank" rel="noopener" class="notification_link">click here</a>`
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
export function formatGc16(raw) {
|
|
141
|
+
const digits = String(raw || '')
|
|
142
|
+
.replace(/\D/g, '')
|
|
143
|
+
.slice(0, 16);
|
|
144
|
+
return digits.replace(/(\d{4})(?=\d)/g, '$1-');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function formatGBP(value) {
|
|
148
|
+
if (value === null || typeof value === 'undefined' || String(value).trim() === '') {
|
|
149
|
+
return value;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const cleanedValue = String(value).replace(/[^\d.-]/g, '');
|
|
153
|
+
const numberValue = Number(cleanedValue);
|
|
154
|
+
|
|
155
|
+
if (Number.isFinite(numberValue)) {
|
|
156
|
+
return `£${numberValue.toFixed(2)}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function buildHintContext(count, limit) {
|
|
163
|
+
const max = count >= limit;
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
canAdd: !max,
|
|
167
|
+
max,
|
|
168
|
+
hintClass: `giftcard-summary__hint${max ? ' giftcard-summary__hint--max' : ''}`,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function buildSummaryEqualMap(data) {
|
|
173
|
+
return Object.entries(SUMMARY_SELECTORS).map(([key, d]) => {
|
|
174
|
+
const raw = data[key];
|
|
175
|
+
let dataValue = raw;
|
|
176
|
+
|
|
177
|
+
if (key === 'giftCardApplied') {
|
|
178
|
+
if (isZeroAmount(raw)) {
|
|
179
|
+
dataValue = '';
|
|
180
|
+
} else if (raw != null && raw !== '') {
|
|
181
|
+
dataValue = formatGBP(raw);
|
|
182
|
+
if (typeof dataValue === 'string' && dataValue.startsWith('£')) {
|
|
183
|
+
dataValue = `-${dataValue}`;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
if (CURRENCY_KEYS.has(key) && raw != null && raw !== '') {
|
|
188
|
+
dataValue = formatGBP(raw);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
item: d.item,
|
|
194
|
+
itemValue: d.value,
|
|
195
|
+
dataValue,
|
|
196
|
+
...(d.title ? { title: d.title } : {}),
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function isZeroAmount(val) {
|
|
202
|
+
const cleaned = String(val ?? '').replace(/[^\d.]/g, '');
|
|
203
|
+
if (!cleaned) {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
const num = Number(cleaned);
|
|
207
|
+
return Number.isFinite(num) && num === 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// --- API/Event-Dispatching Functions ---
|
|
211
|
+
|
|
212
|
+
export function verifyGiftCard($loader, giftCardNumber, pin) {
|
|
213
|
+
showLoader($loader);
|
|
214
|
+
return new Promise((resolve, reject) => {
|
|
215
|
+
const payload = { giftCardNumber, pin };
|
|
216
|
+
const detail = { resolve, reject, payload };
|
|
217
|
+
const evt = createEvent(ADD_GIFT_CARD, payload, detail);
|
|
218
|
+
window.dispatchEvent(evt);
|
|
219
|
+
}).finally(() => {
|
|
220
|
+
hideLoader($loader);
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function removeGiftCard($loader, index) {
|
|
225
|
+
showLoader($loader);
|
|
226
|
+
return new Promise((resolve, reject) => {
|
|
227
|
+
const payload = { index };
|
|
228
|
+
const detail = { resolve, reject, payload };
|
|
229
|
+
const evt = createEvent(REMOVE_GIFT_CARD, payload, detail);
|
|
230
|
+
window.dispatchEvent(evt);
|
|
231
|
+
}).finally(() => {
|
|
232
|
+
hideLoader($loader);
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function payWithGiftCard() {
|
|
237
|
+
return new Promise((resolve, reject) => {
|
|
238
|
+
const detail = { resolve, reject };
|
|
239
|
+
const evt = createEvent(PAY_WITH_GIFT_CARD, undefined, detail);
|
|
240
|
+
window.dispatchEvent(evt);
|
|
241
|
+
});
|
|
242
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { formatGc16 } from './gift-cards-utils';
|
|
2
|
+
import { clearErrorText, showErrorText } from './validation';
|
|
3
|
+
|
|
4
|
+
export function createGcNumberInputHandler({
|
|
5
|
+
fieldSelector,
|
|
6
|
+
fieldErrorClass,
|
|
7
|
+
errorTextSelector,
|
|
8
|
+
ariaInvalidAttr,
|
|
9
|
+
}) {
|
|
10
|
+
return function handleGcNumberInput(e) {
|
|
11
|
+
const input = e.currentTarget;
|
|
12
|
+
const $input = $(input);
|
|
13
|
+
|
|
14
|
+
const canTrack = typeof input.selectionStart === 'number';
|
|
15
|
+
const atEnd = canTrack && input.selectionStart === input.value.length;
|
|
16
|
+
|
|
17
|
+
const hadErr = $input.closest(fieldSelector).hasClass(fieldErrorClass);
|
|
18
|
+
|
|
19
|
+
input.value = formatGc16(input.value);
|
|
20
|
+
|
|
21
|
+
if (hadErr) {
|
|
22
|
+
clearErrorText(
|
|
23
|
+
$input,
|
|
24
|
+
fieldSelector,
|
|
25
|
+
errorTextSelector,
|
|
26
|
+
fieldErrorClass,
|
|
27
|
+
ariaInvalidAttr
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (atEnd && typeof input.setSelectionRange === 'function') {
|
|
32
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function digitsOnlyKeypress(e) {
|
|
38
|
+
if (!/\d/.test(String.fromCharCode(e.which || e.keyCode))) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function handleGcNumberBlur(
|
|
44
|
+
e,
|
|
45
|
+
{ fieldSelector, errorTextSelector, errorClass, ariaInvalidAttr, numberMessage }
|
|
46
|
+
) {
|
|
47
|
+
const $input = $(e.currentTarget);
|
|
48
|
+
const len = String($input.val() || '').replace(/\D/g, '').length;
|
|
49
|
+
|
|
50
|
+
clearErrorText($input, fieldSelector, errorTextSelector, errorClass, ariaInvalidAttr);
|
|
51
|
+
|
|
52
|
+
if (len < 16) {
|
|
53
|
+
showErrorText(
|
|
54
|
+
$input,
|
|
55
|
+
numberMessage,
|
|
56
|
+
fieldSelector,
|
|
57
|
+
errorTextSelector,
|
|
58
|
+
errorClass,
|
|
59
|
+
ariaInvalidAttr
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function handleGcPinInput(
|
|
65
|
+
e,
|
|
66
|
+
{ fieldSelector, errorTextSelector, errorClass, ariaInvalidAttr }
|
|
67
|
+
) {
|
|
68
|
+
const input = e.currentTarget;
|
|
69
|
+
const $input = $(input);
|
|
70
|
+
|
|
71
|
+
const hadErr = $input.closest(fieldSelector).hasClass(errorClass);
|
|
72
|
+
input.value = String(input.value || '')
|
|
73
|
+
.replace(/\D/g, '')
|
|
74
|
+
.slice(0, 8);
|
|
75
|
+
|
|
76
|
+
if (hadErr) {
|
|
77
|
+
clearErrorText($input, fieldSelector, errorTextSelector, errorClass, ariaInvalidAttr);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function handleGcPinKeypress(e) {
|
|
82
|
+
if (!/\d/.test(String.fromCharCode(e.which || e.keyCode))) e.preventDefault();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function handleGcPinBlur(
|
|
86
|
+
e,
|
|
87
|
+
{ fieldSelector, errorTextSelector, errorClass, ariaInvalidAttr, pinMessage }
|
|
88
|
+
) {
|
|
89
|
+
const $input = $(e.currentTarget);
|
|
90
|
+
const length = String($input.val() || '').replace(/\D/g, '').length;
|
|
91
|
+
|
|
92
|
+
clearErrorText($input, fieldSelector, errorTextSelector, errorClass, ariaInvalidAttr);
|
|
93
|
+
|
|
94
|
+
if (length < 8) {
|
|
95
|
+
showErrorText(
|
|
96
|
+
$input,
|
|
97
|
+
pinMessage,
|
|
98
|
+
fieldSelector,
|
|
99
|
+
errorTextSelector,
|
|
100
|
+
errorClass,
|
|
101
|
+
ariaInvalidAttr
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|