@lancom/shared 0.0.395 → 0.0.397
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/assets/js/utils/gtm.js +3 -3
- package/assets/js/utils/phone.js +134 -0
- package/assets/js/utils/product.js +1 -1
- package/components/asides/contact_us/contact-us.vue +1 -1
- package/components/asides/offer_screen_printing/offer-screen-printing.vue +1 -1
- package/components/checkout/order/address-form/address-form.vue +1 -1
- package/components/checkout/order/order-payment-information/order-payment-information.vue +9 -0
- package/components/common/payment/payment_card/payment-card.vue +7 -0
- package/components/common/payment/payment_card/paypal/paypal.scss +8 -0
- package/components/common/payment/payment_card/paypal/paypal.vue +16 -0
- package/components/common/phone_input/phone-input.vue +28 -7
- package/components/customer/customer_form/customer-form.vue +1 -1
- package/components/modals/failed_conversion_modal/failed-conversion-modal.vue +1 -1
- package/components/modals/order_modal/order-modal.vue +1 -1
- package/components/products_kit/products_kit/products_kit_cart/products-kit-cart.scss +2 -2
- package/components/products_kit/products_kit/products_kit_cart/products-kit-cart.vue +13 -13
- package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_color/products-kit-option-color.scss +15 -0
- package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products-kit-option-products.vue +26 -0
- package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products_kit_option_product/products-kit-option-product.scss +38 -4
- package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products_kit_option_product/products-kit-option-product.vue +3 -2
- package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_size/products-kit-option-size.scss +14 -0
- package/components/quotes/quote_request/quote-request.vue +2 -2
- package/components/quotes/quote_view/quote_product_color_simple_products/quote_product_color_simple_product/quote-product-color-simple-product.vue +3 -1
- package/feeds/google-shopping.js +4 -4
- package/mixins/product-preview.js +4 -0
- package/nuxt.config.js +1 -1
- package/package.json +1 -1
- package/routes/index.js +14 -11
- package/store/productsKit.js +10 -6
package/assets/js/utils/gtm.js
CHANGED
|
@@ -47,7 +47,7 @@ const gtm = {
|
|
|
47
47
|
currency: currency?.isoCode || 'AUD',
|
|
48
48
|
items: (products || []).map(product => {
|
|
49
49
|
return {
|
|
50
|
-
id: product.SKU,
|
|
50
|
+
id: product.SKU,
|
|
51
51
|
item_name: product.name,
|
|
52
52
|
google_business_vertical: 'retail',
|
|
53
53
|
price: product.minPrice
|
|
@@ -111,10 +111,10 @@ const gtm = {
|
|
|
111
111
|
|
|
112
112
|
const { shippingAddress } = order;
|
|
113
113
|
if (shippingAddress) {
|
|
114
|
-
const { fullName, email, phone, city, state, postcode, country, addressLine1, addressLine2 } = shippingAddress;
|
|
114
|
+
const { fullName, email, phone, phoneE164, city, state, postcode, country, addressLine1, addressLine2 } = shippingAddress;
|
|
115
115
|
const [first_name, last_name] = fullName.split(' ');
|
|
116
116
|
event.email = email;
|
|
117
|
-
event.phone_number= phone;
|
|
117
|
+
event.phone_number = phoneE164 || phone;
|
|
118
118
|
event.address = {
|
|
119
119
|
first_name,
|
|
120
120
|
last_name,
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} PhoneNumberValidationResult
|
|
3
|
+
* @property {string} e164Number
|
|
4
|
+
* @property {boolean} isValid
|
|
5
|
+
* @property {boolean} supportsSms
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Transforms a phone number to E.164 format if valid for the specified country.
|
|
10
|
+
* @param {string|null|undefined} originalInput
|
|
11
|
+
* @param {string} country
|
|
12
|
+
* @param {string} [postcode]
|
|
13
|
+
* @returns {PhoneNumberValidationResult}
|
|
14
|
+
*/
|
|
15
|
+
export function formatNumberToE164(originalInput, country) {
|
|
16
|
+
const defaultFailureResult = {
|
|
17
|
+
e164Number: '',
|
|
18
|
+
isValid: false,
|
|
19
|
+
supportsSms: false
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (originalInput === null || originalInput === undefined) {
|
|
23
|
+
return defaultFailureResult;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const countryCodeMap = {
|
|
27
|
+
AU: '+61',
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const normalizedCountry = country.toUpperCase();
|
|
31
|
+
const dialingCode = countryCodeMap[normalizedCountry];
|
|
32
|
+
|
|
33
|
+
let cleanedInput = originalInput.trim().replace(/\s+/g, '').replace(/[-()]/g, '');
|
|
34
|
+
|
|
35
|
+
if (cleanedInput === '') {
|
|
36
|
+
return defaultFailureResult;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (normalizedCountry !== 'AU' || !dialingCode) {
|
|
40
|
+
return defaultFailureResult;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (
|
|
44
|
+
cleanedInput.startsWith('1300') ||
|
|
45
|
+
(cleanedInput.startsWith('13') && cleanedInput.length > 2 && cleanedInput.length <= 6) ||
|
|
46
|
+
cleanedInput.startsWith('1800') ||
|
|
47
|
+
(cleanedInput.startsWith('180') && cleanedInput.length > 3 && cleanedInput.length <= 7)
|
|
48
|
+
) {
|
|
49
|
+
return defaultFailureResult;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
cleanedInput.startsWith(dialingCode.substring(1)) &&
|
|
54
|
+
cleanedInput.length === dialingCode.length - 1 + 9
|
|
55
|
+
) {
|
|
56
|
+
const numPartAfterCountryCode = cleanedInput.substring(dialingCode.length - 1);
|
|
57
|
+
const firstDigitNumPart = numPartAfterCountryCode.charAt(0);
|
|
58
|
+
if (
|
|
59
|
+
/^\d+$/.test(numPartAfterCountryCode) &&
|
|
60
|
+
numPartAfterCountryCode.length === 9 &&
|
|
61
|
+
['2', '3', '4', '7', '8'].includes(firstDigitNumPart)
|
|
62
|
+
) {
|
|
63
|
+
return {
|
|
64
|
+
e164Number: dialingCode + numPartAfterCountryCode,
|
|
65
|
+
isValid: true,
|
|
66
|
+
supportsSms: firstDigitNumPart === '4',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return defaultFailureResult;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (cleanedInput.startsWith(dialingCode)) {
|
|
73
|
+
let numPartAfterDialingCodeRaw = cleanedInput.substring(dialingCode.length);
|
|
74
|
+
if (numPartAfterDialingCodeRaw.startsWith('0')) {
|
|
75
|
+
numPartAfterDialingCodeRaw = numPartAfterDialingCodeRaw.substring(1);
|
|
76
|
+
}
|
|
77
|
+
const firstDigitNumPartFinal = numPartAfterDialingCodeRaw.charAt(0);
|
|
78
|
+
if (
|
|
79
|
+
/^\d+$/.test(numPartAfterDialingCodeRaw) &&
|
|
80
|
+
numPartAfterDialingCodeRaw.length === 9 &&
|
|
81
|
+
['2', '3', '4', '7', '8'].includes(firstDigitNumPartFinal)
|
|
82
|
+
) {
|
|
83
|
+
return {
|
|
84
|
+
e164Number: dialingCode + numPartAfterDialingCodeRaw,
|
|
85
|
+
isValid: true,
|
|
86
|
+
supportsSms: firstDigitNumPartFinal === '4',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return defaultFailureResult;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (cleanedInput.startsWith('04')) {
|
|
93
|
+
const mobilePart = cleanedInput.substring(2);
|
|
94
|
+
if (/^\d+$/.test(mobilePart) && mobilePart.length === 8) {
|
|
95
|
+
return {
|
|
96
|
+
e164Number: dialingCode + '4' + mobilePart,
|
|
97
|
+
isValid: true,
|
|
98
|
+
supportsSms: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return defaultFailureResult;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (cleanedInput.startsWith('0') && cleanedInput.length === 10) {
|
|
105
|
+
const areaCode = cleanedInput.charAt(1);
|
|
106
|
+
const numPart = cleanedInput.substring(2);
|
|
107
|
+
if (
|
|
108
|
+
['2', '3', '7', '8'].includes(areaCode) &&
|
|
109
|
+
/^\d+$/.test(numPart) &&
|
|
110
|
+
numPart.length === 8
|
|
111
|
+
) {
|
|
112
|
+
return {
|
|
113
|
+
e164Number: dialingCode + areaCode + numPart,
|
|
114
|
+
isValid: true,
|
|
115
|
+
supportsSms: false,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return defaultFailureResult;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (cleanedInput.length === 9 && /^\d+$/.test(cleanedInput)) {
|
|
122
|
+
const firstDigit = cleanedInput.charAt(0);
|
|
123
|
+
if (['2', '3', '4', '7', '8'].includes(firstDigit)) {
|
|
124
|
+
return {
|
|
125
|
+
e164Number: dialingCode + cleanedInput,
|
|
126
|
+
isValid: true,
|
|
127
|
+
supportsSms: firstDigit === '4',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return defaultFailureResult;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return defaultFailureResult;
|
|
134
|
+
}
|
|
@@ -187,7 +187,7 @@ export function generateProductLink(product, color, toEditor = false, size) {
|
|
|
187
187
|
const categoryPath = generateCategoryPath(product.category);
|
|
188
188
|
if (categoryPath) {
|
|
189
189
|
const categoryBaseLink = `/c/${categoryPath}/${product.alias}/${product.SKU.toLowerCase()}`;
|
|
190
|
-
link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}
|
|
190
|
+
link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}.html`;
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
const query = [];
|
|
@@ -42,6 +42,14 @@
|
|
|
42
42
|
Apple Pay
|
|
43
43
|
</span>
|
|
44
44
|
</label>
|
|
45
|
+
<label
|
|
46
|
+
class="form-label OrderPaymentInformation__checkbox"
|
|
47
|
+
@click="updatePaymentType('paypal')">
|
|
48
|
+
<checked-icon :checked="paymentMethod === 'paypal'" />
|
|
49
|
+
<span class="lc_regular12 lc__grey1 OrderPaymentInformation__checkbox-label">
|
|
50
|
+
Paypal
|
|
51
|
+
</span>
|
|
52
|
+
</label>
|
|
45
53
|
</div>
|
|
46
54
|
<div
|
|
47
55
|
v-if="paymentMethod === 'deposit'"
|
|
@@ -80,6 +88,7 @@
|
|
|
80
88
|
:key="paymentMethod"
|
|
81
89
|
:google="paymentMethod === 'google'"
|
|
82
90
|
:apple="paymentMethod === 'apple'"
|
|
91
|
+
:paypal="paymentMethod === 'paypal'"
|
|
83
92
|
:amount="cartPricing.totalPrice"
|
|
84
93
|
:order="orderData"
|
|
85
94
|
@inited="initedCard">
|
|
@@ -17,6 +17,7 @@ import { mapGetters } from 'vuex';
|
|
|
17
17
|
export default {
|
|
18
18
|
name: 'PaymentCard',
|
|
19
19
|
components: {
|
|
20
|
+
Paypal: () => import('./paypal/paypal'),
|
|
20
21
|
Applepay: () => import('./applepay/applepay'),
|
|
21
22
|
Googlepay: () => import('./googlepay/googlepay'),
|
|
22
23
|
Pinpayment: () => import('./pinpayment/pinpayment'),
|
|
@@ -36,6 +37,9 @@ export default {
|
|
|
36
37
|
},
|
|
37
38
|
apple: {
|
|
38
39
|
type: Boolean
|
|
40
|
+
},
|
|
41
|
+
paypal: {
|
|
42
|
+
type: Boolean
|
|
39
43
|
}
|
|
40
44
|
},
|
|
41
45
|
computed: {
|
|
@@ -47,6 +51,9 @@ export default {
|
|
|
47
51
|
if (this.apple) {
|
|
48
52
|
return 'applepay';
|
|
49
53
|
}
|
|
54
|
+
if (this.paypal) {
|
|
55
|
+
return 'paypal';
|
|
56
|
+
}
|
|
50
57
|
return this.payment?.type || 'stripe-payment';
|
|
51
58
|
}
|
|
52
59
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div>
|
|
3
|
+
<div>{{ item }}</div>
|
|
3
4
|
<div class="form-row">
|
|
4
5
|
<label
|
|
5
6
|
v-if="labelless"
|
|
@@ -63,6 +64,7 @@
|
|
|
63
64
|
|
|
64
65
|
<script>
|
|
65
66
|
import Multiselect from 'vue-multiselect';
|
|
67
|
+
import { formatNumberToE164 } from '@lancom/shared/assets/js/utils/phone';
|
|
66
68
|
|
|
67
69
|
export default {
|
|
68
70
|
name: 'PhoneInput',
|
|
@@ -73,8 +75,9 @@ export default {
|
|
|
73
75
|
codeCountry: {
|
|
74
76
|
type: Object
|
|
75
77
|
},
|
|
76
|
-
|
|
77
|
-
type:
|
|
78
|
+
item: {
|
|
79
|
+
type: Object,
|
|
80
|
+
required: true
|
|
78
81
|
},
|
|
79
82
|
labelText: {
|
|
80
83
|
type: String
|
|
@@ -86,6 +89,14 @@ export default {
|
|
|
86
89
|
type: String,
|
|
87
90
|
default: 'Phone'
|
|
88
91
|
},
|
|
92
|
+
phoneProperty: {
|
|
93
|
+
type: String,
|
|
94
|
+
default: 'phone'
|
|
95
|
+
},
|
|
96
|
+
phoneE164Property: {
|
|
97
|
+
type: String,
|
|
98
|
+
default: 'phoneE164'
|
|
99
|
+
},
|
|
89
100
|
copyFrom: {
|
|
90
101
|
type: Boolean,
|
|
91
102
|
default: false
|
|
@@ -120,16 +131,26 @@ export default {
|
|
|
120
131
|
},
|
|
121
132
|
model: {
|
|
122
133
|
get() {
|
|
123
|
-
return this.
|
|
134
|
+
return this.item[this.phoneProperty];
|
|
124
135
|
},
|
|
125
136
|
set(phone) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
// this.$emit('input', `${this.code}${phone}`);
|
|
137
|
+
this.$set(this.item, this.phoneProperty, phone);
|
|
138
|
+
this.updatePhoneE164();
|
|
139
|
+
|
|
130
140
|
this.$emit('input', phone);
|
|
131
141
|
}
|
|
132
142
|
}
|
|
143
|
+
},
|
|
144
|
+
mounted() {
|
|
145
|
+
this.updatePhoneE164();
|
|
146
|
+
},
|
|
147
|
+
methods: {
|
|
148
|
+
updatePhoneE164() {
|
|
149
|
+
const code = this.codeCountry?.isoCode || this.codeCountry || 'AU';
|
|
150
|
+
const result = formatNumberToE164(this.model, code);
|
|
151
|
+
const e164Number = result?.isValid ? result.e164Number : null;
|
|
152
|
+
this.$set(this.item, this.phoneE164Property, e164Number);
|
|
153
|
+
}
|
|
133
154
|
}
|
|
134
155
|
};
|
|
135
156
|
</script>
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="ProductsKitCart__wrapper">
|
|
3
3
|
<div class="ProductsKitCart__products">
|
|
4
|
-
<products-kit-cart-options :
|
|
5
|
-
<div class="ProductsKitCart__qty">
|
|
6
|
-
<number-input
|
|
7
|
-
v-model="kitQty"
|
|
8
|
-
:disabled="addingToCart || !isAllOptionsSelected" />
|
|
9
|
-
</div>
|
|
10
|
-
</div>
|
|
11
|
-
<div class="ProductsKitCart__info">
|
|
4
|
+
<products-kit-cart-options :products-kit="productsKit" />
|
|
12
5
|
<div
|
|
13
6
|
v-if="isAllOptionsSelected"
|
|
14
7
|
class="ProductsKitCart__price">
|
|
15
8
|
{{ totalPrice | price }}
|
|
16
9
|
</div>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="ProductsKitCart__info">
|
|
12
|
+
<div class="ProductsKitCart__qty">
|
|
13
|
+
<number-input
|
|
14
|
+
v-model="kitQty"
|
|
15
|
+
:disabled="addingToCart || !isAllOptionsSelected" />
|
|
16
|
+
</div>
|
|
17
17
|
<div>
|
|
18
18
|
<btn
|
|
19
|
-
:btn-disabled="addingToCart || !isAllOptionsSelected"
|
|
19
|
+
:btn-disabled="addingToCart || !isAllOptionsSelected || !kitQty"
|
|
20
20
|
btn-class="green"
|
|
21
|
-
btn-label="ADD TO CART"
|
|
22
|
-
@onclick="proceedToCard()"/>
|
|
21
|
+
btn-label="ADD TO CART"
|
|
22
|
+
@onclick="proceedToCard()" />
|
|
23
23
|
</div>
|
|
24
24
|
</div>
|
|
25
25
|
</div>
|
|
@@ -109,10 +109,10 @@ export default {
|
|
|
109
109
|
};
|
|
110
110
|
await this.addToCart(data);
|
|
111
111
|
this.$toastr.s('Products Kit successfully added to cart');
|
|
112
|
-
this.kitQty =
|
|
112
|
+
this.kitQty = 0;
|
|
113
113
|
this.clearOptions();
|
|
114
114
|
this.clearProductsKitPricing();
|
|
115
|
-
} catch (e) {
|
|
115
|
+
} catch (e) {
|
|
116
116
|
console.log(e);
|
|
117
117
|
} finally {
|
|
118
118
|
this.addingToCart = false;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
</template>
|
|
12
12
|
|
|
13
13
|
<script>
|
|
14
|
+
import { mapGetters, mapActions } from 'vuex';
|
|
14
15
|
import ProductsKitOptionProduct from './products_kit_option_product/products-kit-option-product';
|
|
15
16
|
|
|
16
17
|
export default {
|
|
@@ -27,6 +28,31 @@ export default {
|
|
|
27
28
|
type: Object,
|
|
28
29
|
required: true
|
|
29
30
|
}
|
|
31
|
+
},
|
|
32
|
+
computed: {
|
|
33
|
+
...mapGetters([
|
|
34
|
+
'country',
|
|
35
|
+
'currency',
|
|
36
|
+
'stockCountry'
|
|
37
|
+
])
|
|
38
|
+
},
|
|
39
|
+
mounted() {
|
|
40
|
+
if (this.option.required && this.option.products?.length === 1) {
|
|
41
|
+
this.selectProduct(this.option.products[0]);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
methods: {
|
|
45
|
+
...mapActions('productsKit', ['selectOptionProduct']),
|
|
46
|
+
selectProduct(product) {
|
|
47
|
+
const params = {
|
|
48
|
+
country: this.country?._id,
|
|
49
|
+
currency: this.currency?._id,
|
|
50
|
+
stockCountry: this.stockCountry?._id,
|
|
51
|
+
option: this.option,
|
|
52
|
+
product
|
|
53
|
+
};
|
|
54
|
+
this.selectOptionProduct(params);
|
|
55
|
+
}
|
|
30
56
|
}
|
|
31
57
|
};
|
|
32
58
|
</script>
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
&__preview {
|
|
11
|
-
width:
|
|
12
|
-
height:
|
|
13
|
-
margin:
|
|
11
|
+
width: 175px;
|
|
12
|
+
height: 290px;
|
|
13
|
+
margin: 7px;
|
|
14
14
|
position: relative;
|
|
15
15
|
z-index: 1;
|
|
16
16
|
&--without-price {
|
|
17
|
-
height:
|
|
17
|
+
height: 340px;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
&__select {
|
|
@@ -38,4 +38,38 @@
|
|
|
38
38
|
font-weight: bold;
|
|
39
39
|
z-index: 2;
|
|
40
40
|
}
|
|
41
|
+
}
|
|
42
|
+
::v-deep {
|
|
43
|
+
.ProductPreview {
|
|
44
|
+
&__link {
|
|
45
|
+
height: 175px;
|
|
46
|
+
&-cover,
|
|
47
|
+
&-cover-hover {
|
|
48
|
+
height: 175px;
|
|
49
|
+
top: 10px;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
&__sku {
|
|
53
|
+
font-size: 13px;
|
|
54
|
+
}
|
|
55
|
+
&__name {
|
|
56
|
+
font-size: 15px;
|
|
57
|
+
line-height: 20px;
|
|
58
|
+
margin-top: 1px;
|
|
59
|
+
min-height: 35px;
|
|
60
|
+
}
|
|
61
|
+
&__prices-printed-price {
|
|
62
|
+
font-size: 16px;
|
|
63
|
+
line-height: 20px;
|
|
64
|
+
}
|
|
65
|
+
&__slogan-item {
|
|
66
|
+
span {
|
|
67
|
+
padding: 2px 3px;
|
|
68
|
+
font-size: 8px;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
&__content {
|
|
72
|
+
padding: 10px 10px 0px 10px;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
41
75
|
}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
ref="preview"
|
|
14
14
|
:product="product"
|
|
15
15
|
:to-editor="false"
|
|
16
|
+
:visible-color-thumbs="false"
|
|
16
17
|
:visible-prices="!option.required"
|
|
17
18
|
@preview="onPreviewProduct($event)"
|
|
18
19
|
@color="onProductSelectColor($event)" />
|
|
@@ -22,7 +23,7 @@
|
|
|
22
23
|
:btn-block="true"
|
|
23
24
|
@onclick="selectProduct"
|
|
24
25
|
:btn-class="isSelectedProduct ? 'green' : 'gray'"
|
|
25
|
-
btn-label="SELECT" />
|
|
26
|
+
:btn-label="isSelectedProduct ? 'DESELECT' : 'SELECT'" />
|
|
26
27
|
<div
|
|
27
28
|
v-if="isSelectedProduct"
|
|
28
29
|
class="ProductsKitOptionProduct__config mt-5">
|
|
@@ -129,7 +130,7 @@ export default {
|
|
|
129
130
|
currency: this.currency?._id,
|
|
130
131
|
stockCountry: this.stockCountry?._id,
|
|
131
132
|
option: this.option,
|
|
132
|
-
product: this.product
|
|
133
|
+
product: this.isSelectedProduct ? null : this.product
|
|
133
134
|
};
|
|
134
135
|
this.selectOptionProduct(params);
|
|
135
136
|
},
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
</validation-provider>
|
|
38
38
|
<phone-input
|
|
39
39
|
class="form-row"
|
|
40
|
-
|
|
40
|
+
:item="quote.address" />
|
|
41
41
|
<validation-provider
|
|
42
42
|
v-slot="{ errors }"
|
|
43
43
|
tag="div"
|
|
@@ -427,7 +427,7 @@ export default {
|
|
|
427
427
|
}))
|
|
428
428
|
};
|
|
429
429
|
})
|
|
430
|
-
}
|
|
430
|
+
}
|
|
431
431
|
};
|
|
432
432
|
const body = {
|
|
433
433
|
recaptchaToken,
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
class="form-field centered QuoteProductColorSimpleProduct__field"
|
|
16
16
|
:class="{
|
|
17
17
|
empty: !model,
|
|
18
|
-
error: model > simpleProduct.quantityStock
|
|
18
|
+
error: model > simpleProduct.quantityStock && !quote.ignoreStockAvailability
|
|
19
19
|
}"
|
|
20
20
|
@wheel="onWheel"
|
|
21
21
|
@change="onChange()" />
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
</template>
|
|
37
37
|
|
|
38
38
|
<script>
|
|
39
|
+
import { mapGetters } from 'vuex';
|
|
39
40
|
import confirm from '@lancom/shared/mixins/confirm';
|
|
40
41
|
import { inRange, price } from '@lancom/shared/assets/js/utils/filters';
|
|
41
42
|
|
|
@@ -52,6 +53,7 @@ export default {
|
|
|
52
53
|
}
|
|
53
54
|
},
|
|
54
55
|
computed: {
|
|
56
|
+
...mapGetters('quote', ['quote']),
|
|
55
57
|
model: {
|
|
56
58
|
get() {
|
|
57
59
|
return this.simpleProduct.amount;
|
package/feeds/google-shopping.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
async function googleLocalShoppingFeed(axios, config, availableStores, country) {
|
|
2
2
|
const result = await googleShoppingFeed(axios, config, availableStores, country, false, { allowSameDayDelivery: true });
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
const fields = [
|
|
5
5
|
'g:availability',
|
|
6
6
|
'g:id',
|
|
@@ -59,7 +59,7 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
|
|
|
59
59
|
const description = `${product.description || product.fabricInfoShort || product.name || ''}`
|
|
60
60
|
.replace(/ /g, ' ')
|
|
61
61
|
.replace(/·/, '·');
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
let link = `https://${config.HOST_NAME}${generateProductLink(product, sp.color, isEditor)}`;
|
|
64
64
|
link = link.includes('?') ? `${link}&size=${sp.size?.shortName}` : `${link}?size=${sp.size?.shortName}`
|
|
65
65
|
link = link.includes('?') ? `${link}&price=IT` : `${link}?price=IT`
|
|
@@ -186,7 +186,7 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
|
|
|
186
186
|
];
|
|
187
187
|
}, [])
|
|
188
188
|
};
|
|
189
|
-
|
|
189
|
+
|
|
190
190
|
return {
|
|
191
191
|
_declaration: { _attributes: { version: "1.0", encoding: "utf-8" } },
|
|
192
192
|
rss: {
|
|
@@ -221,7 +221,7 @@ function generateProductLink(product, color, toEditor) {
|
|
|
221
221
|
const categoryPath = generateCategoryPath(product.category);
|
|
222
222
|
if (categoryPath) {
|
|
223
223
|
const categoryBaseLink = `/c/${categoryPath}/${product.alias}/${product.SKU.toLowerCase()}`;
|
|
224
|
-
link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}
|
|
224
|
+
link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}.html`;
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
return `${link}${color ? `?color=${color.alias || color}` : ''}`;
|
package/nuxt.config.js
CHANGED
|
@@ -138,7 +138,7 @@ module.exports = (config, axios, { raygunClient, publicPath, productUrlToEditor
|
|
|
138
138
|
router: {
|
|
139
139
|
extendRoutes(routes, resolve) {
|
|
140
140
|
return [
|
|
141
|
-
...sharedRoutes(routes, resolve),
|
|
141
|
+
...sharedRoutes(routes, resolve, process.env),
|
|
142
142
|
...routes,
|
|
143
143
|
];
|
|
144
144
|
}
|
package/package.json
CHANGED
package/routes/index.js
CHANGED
|
@@ -1,52 +1,55 @@
|
|
|
1
|
-
module.exports = function(routes, resolve) {
|
|
1
|
+
module.exports = function(routes, resolve, config) {
|
|
2
|
+
const PRODUCT_LINK_EXTENSION = config?.PRODUCT_LINK_EXTENSION || '/info.html';
|
|
3
|
+
const PRODUCT_EDITOR_LINK_EXTENSION = config?.PRODUCT_EDITOR_LINK_EXTENSION || '.html';
|
|
4
|
+
|
|
2
5
|
return [{
|
|
3
6
|
name: 'product-taxonomy-view-level1',
|
|
4
|
-
path:
|
|
7
|
+
path: `/c/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
|
|
5
8
|
component: resolve('@/pages/_brand/_type/_slug/index.vue'),
|
|
6
9
|
chunkName: 'pages/product/view/level1'
|
|
7
10
|
}, {
|
|
8
11
|
name: 'product-taxonomy-view-level2',
|
|
9
|
-
path:
|
|
12
|
+
path: `/c/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
|
|
10
13
|
component: resolve('@/pages/_brand/_type/_slug/index.vue'),
|
|
11
14
|
chunkName: 'pages/product/view/level2'
|
|
12
15
|
}, {
|
|
13
16
|
name: 'product-taxonomy-view-level3',
|
|
14
|
-
path:
|
|
17
|
+
path: `/c/:category2/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
|
|
15
18
|
component: resolve('@/pages/_brand/_type/_slug/index.vue'),
|
|
16
19
|
chunkName: 'pages/product/view/level3'
|
|
17
20
|
}, {
|
|
18
21
|
name: 'product-taxonomy-view-level4',
|
|
19
|
-
path:
|
|
22
|
+
path: `/c/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
|
|
20
23
|
component: resolve('@/pages/_brand/_type/_slug/index.vue'),
|
|
21
24
|
chunkName: 'pages/product/view/level4'
|
|
22
25
|
}, {
|
|
23
26
|
name: 'product-taxonomy-view-level5',
|
|
24
|
-
path:
|
|
27
|
+
path: `/c/:category4/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
|
|
25
28
|
component: resolve('@/pages/_brand/_type/_slug/index.vue'),
|
|
26
29
|
chunkName: 'pages/product/view/level5'
|
|
27
30
|
}, {
|
|
28
31
|
name: 'product-taxonomy-editor-level1',
|
|
29
|
-
path:
|
|
32
|
+
path: `/c/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
|
|
30
33
|
component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
|
|
31
34
|
chunkName: 'pages/product/editor/level1'
|
|
32
35
|
}, {
|
|
33
36
|
name: 'product-taxonomy-editor-level2',
|
|
34
|
-
path:
|
|
37
|
+
path: `/c/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
|
|
35
38
|
component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
|
|
36
39
|
chunkName: 'pages/product/editor/level2'
|
|
37
40
|
}, {
|
|
38
41
|
name: 'product-taxonomy-editor-level3',
|
|
39
|
-
path:
|
|
42
|
+
path: `/c/:category2/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
|
|
40
43
|
component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
|
|
41
44
|
chunkName: 'pages/product/editor/level3'
|
|
42
45
|
}, {
|
|
43
46
|
name: 'product-taxonomy-editor-level4',
|
|
44
|
-
path:
|
|
47
|
+
path: `/c/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
|
|
45
48
|
component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
|
|
46
49
|
chunkName: 'pages/product/editor/level4'
|
|
47
50
|
}, {
|
|
48
51
|
name: 'product-taxonomy-editor-level5',
|
|
49
|
-
path:
|
|
52
|
+
path: `/c/:category4/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
|
|
50
53
|
component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
|
|
51
54
|
chunkName: 'pages/product/editor/level5'
|
|
52
55
|
}, {
|
package/store/productsKit.js
CHANGED
|
@@ -5,7 +5,7 @@ import { generateGUID } from '@lancom/shared/assets/js/utils/guid';
|
|
|
5
5
|
import Vue from 'vue';
|
|
6
6
|
|
|
7
7
|
export const state = () => ({
|
|
8
|
-
amount:
|
|
8
|
+
amount: 0,
|
|
9
9
|
productsKit: null,
|
|
10
10
|
loadError: null,
|
|
11
11
|
selectedOptionsProducts: {},
|
|
@@ -39,10 +39,14 @@ export const getters = {
|
|
|
39
39
|
selectedOptionsSimpleProducts: ({ selectedOptionsSimpleProducts }) => selectedOptionsSimpleProducts,
|
|
40
40
|
selectedOptionsColors: ({ selectedOptionsColors }) => selectedOptionsColors,
|
|
41
41
|
selectedOptionsSizes: ({ selectedOptionsSizes }) => selectedOptionsSizes,
|
|
42
|
-
isAllOptionsSelected: ({ productsKit,
|
|
43
|
-
const selectedSimpleProducts = getSelectedSimpleProducts(selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes);
|
|
42
|
+
isAllOptionsSelected: ({ productsKit, selectedOptionsColors, selectedOptionsSizes, selectedOptionsProducts }) => {
|
|
44
43
|
const requiredOptions = productsKit.productsKitOptions?.filter(pk => pk.required) || [];
|
|
45
|
-
|
|
44
|
+
const isAllRequieredSelected = requiredOptions.every(o => !!selectedOptionsProducts[o._id] && !!selectedOptionsColors[o._id] && !!selectedOptionsSizes[o._id]);
|
|
45
|
+
|
|
46
|
+
const selectedNotRequiredOptions = productsKit.productsKitOptions?.filter(pk => !pk.required && selectedOptionsProducts[pk._id]) || [];
|
|
47
|
+
const isAllNotRequieredSelected = selectedNotRequiredOptions.every(o => !!selectedOptionsColors[o._id] && !!selectedOptionsSizes[o._id]);
|
|
48
|
+
|
|
49
|
+
return isAllRequieredSelected && isAllNotRequieredSelected;
|
|
46
50
|
},
|
|
47
51
|
selectedSimpleProducts: ({ selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes }) => getSelectedSimpleProducts(selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes),
|
|
48
52
|
maxOrderQty: ({ selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes }) => {
|
|
@@ -93,7 +97,7 @@ export const actions = {
|
|
|
93
97
|
},
|
|
94
98
|
async selectOptionProduct({ commit }, { option, product, country, currency, stockCountry }) {
|
|
95
99
|
const params = { country, currency, stockCountry };
|
|
96
|
-
const simpleProducts = await api.fetchProductDetails(null, product.alias, params);
|
|
100
|
+
const simpleProducts = product ? (await api.fetchProductDetails(null, product.alias, params)) : [];
|
|
97
101
|
|
|
98
102
|
commit('setSelectedOptionProduct', { option, product });
|
|
99
103
|
commit('setSelectedOptionSimpleProducts', { option, simpleProducts });
|
|
@@ -137,7 +141,7 @@ export const actions = {
|
|
|
137
141
|
commit('setCalculatingPrice', false);
|
|
138
142
|
},
|
|
139
143
|
updateAmount({ getters, commit }, amount) {
|
|
140
|
-
const value = Math.min(getters.maxOrderQty, Math.max(amount,
|
|
144
|
+
const value = Math.min(getters.maxOrderQty, Math.max(amount, 0));
|
|
141
145
|
commit('setAmount', value);
|
|
142
146
|
}
|
|
143
147
|
};
|