@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.
Files changed (29) hide show
  1. package/assets/js/utils/gtm.js +3 -3
  2. package/assets/js/utils/phone.js +134 -0
  3. package/assets/js/utils/product.js +1 -1
  4. package/components/asides/contact_us/contact-us.vue +1 -1
  5. package/components/asides/offer_screen_printing/offer-screen-printing.vue +1 -1
  6. package/components/checkout/order/address-form/address-form.vue +1 -1
  7. package/components/checkout/order/order-payment-information/order-payment-information.vue +9 -0
  8. package/components/common/payment/payment_card/payment-card.vue +7 -0
  9. package/components/common/payment/payment_card/paypal/paypal.scss +8 -0
  10. package/components/common/payment/payment_card/paypal/paypal.vue +16 -0
  11. package/components/common/phone_input/phone-input.vue +28 -7
  12. package/components/customer/customer_form/customer-form.vue +1 -1
  13. package/components/modals/failed_conversion_modal/failed-conversion-modal.vue +1 -1
  14. package/components/modals/order_modal/order-modal.vue +1 -1
  15. package/components/products_kit/products_kit/products_kit_cart/products-kit-cart.scss +2 -2
  16. package/components/products_kit/products_kit/products_kit_cart/products-kit-cart.vue +13 -13
  17. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_color/products-kit-option-color.scss +15 -0
  18. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products-kit-option-products.vue +26 -0
  19. 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
  20. 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
  21. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_size/products-kit-option-size.scss +14 -0
  22. package/components/quotes/quote_request/quote-request.vue +2 -2
  23. package/components/quotes/quote_view/quote_product_color_simple_products/quote_product_color_simple_product/quote-product-color-simple-product.vue +3 -1
  24. package/feeds/google-shopping.js +4 -4
  25. package/mixins/product-preview.js +4 -0
  26. package/nuxt.config.js +1 -1
  27. package/package.json +1 -1
  28. package/routes/index.js +14 -11
  29. package/store/productsKit.js +10 -6
@@ -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}/info.html`;
190
+ link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}.html`;
191
191
  }
192
192
  }
193
193
  const query = [];
@@ -69,7 +69,7 @@
69
69
  <phone-input
70
70
  :labelless="false"
71
71
  :required="requiredPhone"
72
- v-model="form.phone" />
72
+ :item="form" />
73
73
  <validation-provider
74
74
  v-slot="{ errors }"
75
75
  tag="div"
@@ -68,7 +68,7 @@
68
68
  </validation-provider>
69
69
  <phone-input
70
70
  :labelless="false"
71
- v-model="form.phone" />
71
+ :item="form" />
72
72
  <validation-provider
73
73
  v-slot="{ errors }"
74
74
  tag="div"
@@ -93,7 +93,7 @@
93
93
  {{ errors[0] }}
94
94
  </span>
95
95
  </validation-provider>
96
- <phone-input v-model="address.phone" class="form-col col-half" />
96
+ <phone-input :item="address" class="form-col col-half" />
97
97
  </div>
98
98
  <validation-provider
99
99
  v-slot="{ errors }"
@@ -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
  },
@@ -0,0 +1,8 @@
1
+ @import "@/assets/scss/ui_kit";
2
+ @import "@/assets/scss/variables";
3
+
4
+ .Payment {
5
+ &__wrapper {
6
+ font-weight: bold;
7
+ }
8
+ }
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <div class="Payment__wrapper">
3
+ Temporarily Not Available
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'Paypal'
10
+ };
11
+ </script>
12
+
13
+
14
+ <style lang="scss" scoped>
15
+ @import 'paypal.scss';
16
+ </style>
@@ -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
- value: {
77
- type: String
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.value; // (this.value || '').replace(/^\+[0-9]{2}/, '');
134
+ return this.item[this.phoneProperty];
124
135
  },
125
136
  set(phone) {
126
- // if (this.code === '+61' && phone?.length === 10 && phone[0] === '0') {
127
- // phone = phone.slice(1);
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>
@@ -133,7 +133,7 @@
133
133
  <div class="row">
134
134
  <div class="col-6">
135
135
  <phone-input
136
- v-model="customer.phone"
136
+ :item="customer"
137
137
  label-text="Phone (optional)" />
138
138
  </div>
139
139
  <div class="col-6">
@@ -52,7 +52,7 @@
52
52
  </span>
53
53
  </validation-provider>
54
54
  <phone-input
55
- v-model="form.phone"
55
+ :item="form"
56
56
  class="form-col col-half" />
57
57
  </div>
58
58
  <validation-provider
@@ -41,7 +41,7 @@
41
41
  </span>
42
42
  </validation-provider>
43
43
  <phone-input
44
- v-model="form.phone"
44
+ :item="form"
45
45
  class="form-col col-half" />
46
46
  </div>
47
47
  <validation-provider
@@ -17,10 +17,10 @@
17
17
  margin: 0 20px;
18
18
  }
19
19
  &__qty {
20
- margin-left: 10px;
20
+ margin-right: 10px;
21
21
  ::v-deep {
22
22
  input {
23
- padding: 14px;
23
+ padding: 11px;
24
24
  font-size: 20px;
25
25
  width: 110px;
26
26
  }
@@ -1,25 +1,25 @@
1
1
  <template>
2
2
  <div class="ProductsKitCart__wrapper">
3
3
  <div class="ProductsKitCart__products">
4
- <products-kit-cart-options :productsKit="productsKit" />
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 = 1;
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;
@@ -1 +1,16 @@
1
1
  @import "@/assets/scss/variables";
2
+
3
+ ::v-deep {
4
+ .multiselect {
5
+ min-height: 20px;
6
+ &__tags {
7
+ padding: 5px;
8
+ min-height: 20px;
9
+ }
10
+ &__single {
11
+ font-size: 14px;
12
+ line-height: 20px;
13
+ margin: 3px 0 0 4px;
14
+ }
15
+ }
16
+ }
@@ -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: 250px;
12
- height: 490px;
13
- margin: 0 0 15px 15px;
11
+ width: 175px;
12
+ height: 290px;
13
+ margin: 7px;
14
14
  position: relative;
15
15
  z-index: 1;
16
16
  &--without-price {
17
- height: 440px;
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
  },
@@ -1 +1,15 @@
1
1
  @import "@/assets/scss/variables";
2
+
3
+ ::v-deep {
4
+ .multiselect {
5
+ min-height: 20px;
6
+ &__tags {
7
+ padding: 8px 5px 5px 9px;
8
+ min-height: 20px;
9
+ }
10
+ &__single {
11
+ font-size: 14px;
12
+ line-height: 20px;
13
+ }
14
+ }
15
+ }
@@ -37,7 +37,7 @@
37
37
  </validation-provider>
38
38
  <phone-input
39
39
  class="form-row"
40
- v-model="quote.address.phone" />
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;
@@ -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(/&nbsp;/g, ' ')
61
61
  .replace(/&middot;/, '·');
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}/info.html`;
224
+ link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}.html`;
225
225
  }
226
226
  }
227
227
  return `${link}${color ? `?color=${color.alias || color}` : ''}`;
@@ -29,6 +29,10 @@ const productPreview = {
29
29
  visiblePrices: {
30
30
  type: Boolean,
31
31
  default: true
32
+ },
33
+ visibleColorThumbs: {
34
+ type: Boolean,
35
+ default: true
32
36
  }
33
37
  },
34
38
  data() {
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.395",
3
+ "version": "0.0.397",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
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: '/c/:category/:slug/:sku/info.html',
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: '/c/:category1/:category/:slug/:sku/info.html',
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: '/c/:category2/:category1/:category/:slug/:sku/info.html',
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: '/c/:category3/:category2/:category1/:category/:slug/:sku/info.html',
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: '/c/:category4/:category3/:category2/:category1/:category/:slug/:sku/info.html',
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: '/c/:category/:slug/:sku.html',
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: '/c/:category1/:category/:slug/:sku.html',
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: '/c/:category2/:category1/:category/:slug/:sku.html',
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: '/c/:category3/:category2/:category1/:category/:slug/:sku.html',
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: '/c/:category4/:category3/:category2/:category1/:category/:slug/:sku.html',
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
  }, {
@@ -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: 1,
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, selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes }) => {
43
- const selectedSimpleProducts = getSelectedSimpleProducts(selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes);
42
+ isAllOptionsSelected: ({ productsKit, selectedOptionsColors, selectedOptionsSizes, selectedOptionsProducts }) => {
44
43
  const requiredOptions = productsKit.productsKitOptions?.filter(pk => pk.required) || [];
45
- return requiredOptions?.length <= selectedSimpleProducts?.length;
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, 1));
144
+ const value = Math.min(getters.maxOrderQty, Math.max(amount, 0));
141
145
  commit('setAmount', value);
142
146
  }
143
147
  };