@lancom/shared 0.0.386 → 0.0.388

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 (31) hide show
  1. package/assets/scss/variables/_theme.scss +1 -0
  2. package/components/checkout/cart/cart.mixin.js +3 -3
  3. package/components/checkout/cart/cart_entity/cart-entity.mixin.js +8 -0
  4. package/components/checkout/cart/cart_entity/cart-entity.vue +10 -0
  5. package/components/checkout/cart/cart_products_kit_entity/cart-products-kit-entity.vue +4 -1
  6. package/components/checkout/cart/cart_shipping/cart-shipping.scss +5 -0
  7. package/components/checkout/cart/cart_shipping/cart-shipping.vue +45 -10
  8. package/components/checkout/order/address-form/address-form.vue +2 -4
  9. package/components/checkout/order/order-billing-information/order-billing-information.vue +1 -1
  10. package/components/checkout/order/order-shipping-information/order-shipping-information.vue +2 -2
  11. package/components/common/checkbox.vue +1 -1
  12. package/components/common/postcode_select/postcode-select.vue +2 -2
  13. package/components/modals/order_modal/order-modal.vue +3 -5
  14. package/components/product/product_price_range/product-price-range.vue +2 -2
  15. package/components/products_kit/products_kit/products-kit.scss +5 -0
  16. package/components/products_kit/products_kit/products-kit.vue +19 -18
  17. package/components/products_kit/products_kit/products_kit_cart/products-kit-cart.scss +1 -1
  18. package/components/products_kit/products_kit/products_kit_cart/products_kit_cart_options/products-kit-cart-options.vue +1 -1
  19. package/components/products_kit/products_kit/products_kit_cart/products_kit_cart_options/products_kit_cart_option/products-kit-cart-option.scss +6 -0
  20. package/components/products_kit/products_kit/products_kit_cart/products_kit_cart_options/products_kit_cart_option/products-kit-cart-option.vue +8 -1
  21. package/components/products_kit/products_kit/products_kit_options/products-kit-options.vue +5 -1
  22. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_color/products-kit-option-color.vue +5 -0
  23. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_size/products-kit-option-size.vue +7 -0
  24. package/mixins/product-view.js +5 -0
  25. package/package.json +1 -1
  26. package/pages/checkout/order.vue +5 -1
  27. package/routes/index.js +0 -10
  28. package/store/cart.js +16 -5
  29. package/store/productsKit.js +8 -4
  30. package/pages/products-kit/_alias.vue +0 -103
  31. package/pages/products-kit/index.vue +0 -59
@@ -21,6 +21,7 @@ $medium_gray: #B0B0BA !default;
21
21
  $light_gray: #F9F9FC;
22
22
  $link: #5D89A9;
23
23
  $error: #E44868;
24
+ $warning: #e48e48;
24
25
  $secondary_900: #20303C;
25
26
  $secondary_800: #324B5D;
26
27
  $secondary_700: #3D5D74;
@@ -35,7 +35,7 @@ export default {
35
35
  return this.isNotValidProductsQuantity || this.isNotValidStockQuantity || this.isNotValidPrintsQuantity || this.isNotValidPrintsBigSizeQuantity;
36
36
  },
37
37
  isNotValidShipping() {
38
- return (this.needToPickup && this.notValidProductsPickup.length > 0) || !this.suburb;
38
+ return this.needToPickup ? (this.notValidProductsPickup.length > 0 && !this.suburb) : !this.suburb;
39
39
  },
40
40
  isNotValidProductsQuantity() {
41
41
  return this.notValidProductsQuantities.length > 0;
@@ -63,11 +63,11 @@ export default {
63
63
  },
64
64
  methods: {
65
65
  ...mapMutations('cart', [
66
- 'setNeedToPickup',
67
- 'setSuburb',
68
66
  'setCoupon'
69
67
  ]),
70
68
  ...mapActions('cart', [
69
+ 'setSuburb',
70
+ 'setNeedToPickup',
71
71
  'removeSimpleProductsFromCart',
72
72
  'calculateCartPrice'
73
73
  ]),
@@ -25,6 +25,14 @@ export default {
25
25
  productLink() {
26
26
  return generateProductLink(this.product, null, !!this.SETTINGS.CART_PRODUCT_LINK_TO_EDITOR);
27
27
  },
28
+ suppliersWithRates() {
29
+ return this.cartPricing?.shipping?.suppliersWithRates || [];
30
+ },
31
+ fulfillment() {
32
+ const supplierWithRates = this.suppliersWithRates.find(s => s?.brands?.some(b => b?._id === this.product?.brand?._id));
33
+ const selectedRate = supplierWithRates?.rates?.find(r => r.selected);
34
+ return selectedRate?.name;
35
+ },
28
36
  sumPrints() {
29
37
  return (this.entity.prints || []).length;
30
38
  },
@@ -40,6 +40,16 @@
40
40
  {{ product.brand.name }}
41
41
  </div>
42
42
  </div>
43
+ <div
44
+ v-if="fulfillment"
45
+ class="CartEntity__feature">
46
+ <div class="lc_title-small">
47
+ Fulfillment:
48
+ </div>
49
+ <div class="lc_body-small">
50
+ {{ fulfillment }}
51
+ </div>
52
+ </div>
43
53
  </div>
44
54
  <div>
45
55
  <cart-entity-color-simple-products
@@ -28,6 +28,8 @@
28
28
  </template>
29
29
 
30
30
  <script>
31
+ import { mapGetters } from 'vuex';
32
+ import { generateProductLink } from '@lancom/shared/assets/js/utils/product';
31
33
  import CartEntity from './../cart_entity/cart-entity';
32
34
 
33
35
  export default {
@@ -42,8 +44,9 @@ export default {
42
44
  }
43
45
  },
44
46
  computed: {
47
+ ...mapGetters(['SETTINGS']),
45
48
  productsKitLink() {
46
- return `/products-kit/${this.productsKitEntity.productsKit.alias}`;
49
+ return generateProductLink(this.productsKitEntity.productsKit, null, !!this.SETTINGS.CART_PRODUCT_LINK_TO_EDITOR);
47
50
  }
48
51
  },
49
52
  methods: {
@@ -40,4 +40,9 @@
40
40
  font-size: 13px;
41
41
  line-height: 17px;
42
42
  }
43
+ &__pickup-warnings {
44
+ color: $warning;
45
+ font-size: 13px;
46
+ line-height: 17px;
47
+ }
43
48
  }
@@ -2,7 +2,7 @@
2
2
  <div
3
3
  class="CartShipping__wrapper"
4
4
  :class="{
5
- 'CartShipping__wrapper--warning': (needToPickup && hasNotValidProductsPickup) || !suburb
5
+ 'CartShipping__wrapper--warning': needToPickup ? hasNotValidProductsPickup : !suburb
6
6
  }">
7
7
  <div class="lc_h4 lc_uppercase lc_black CartShipping__header">
8
8
  Order Shipping
@@ -18,6 +18,7 @@
18
18
  @select="$emit('suburb', $event)" />
19
19
  <div class="mt-10 mb-10">
20
20
  <checkbox
21
+ :disabled="!isAvailableForPickupSomeProducts"
21
22
  v-model="wantsToPickup"
22
23
  :dark="true"
23
24
  @change="calculatePrice()">
@@ -26,15 +27,20 @@
26
27
  </div>
27
28
  </checkbox>
28
29
  </div>
30
+ <div
31
+ v-if="!isAvailableForPickupSomeProducts"
32
+ class="CartShipping__pickup-warnings">
33
+ Pickup only available for "Code Red" brand
34
+ </div>
29
35
  <div
30
36
  v-if="visiblePickupError && hasNotValidProductsPickup"
31
37
  class="CartShipping__pickup-errors">
32
- <div>Not all items in cart are available for pickup. Some items are available for delivery only</div>
38
+ <div>Not all items in cart are available for pickup. Some items are available for delivery only:</div>
33
39
  <ul>
34
40
  <li
35
- v-for="product in notValidProductsPickup"
41
+ v-for="product in notValidProductsPickupBySKU"
36
42
  :key="product._id">
37
- <b>{{ product.name }}. {{ product.size ? product.size.shortName : '' }}</b> order quantity: {{ product.amount }}. Available {{ product.warehouseQuantityStock }}
43
+ <b>{{ product.name }}({{ product.productSKU }})</b>
38
44
  </li>
39
45
  </ul>
40
46
  </div>
@@ -47,7 +53,7 @@ import { mapGetters, createNamespacedHelpers } from 'vuex';
47
53
  import PostcodeSelect from '@lancom/shared/components/common/postcode_select/postcode-select';
48
54
  import { price } from '@lancom/shared/assets/js/utils/filters';
49
55
 
50
- const { mapActions, mapMutations } = createNamespacedHelpers('cart');
56
+ const { mapActions } = createNamespacedHelpers('cart');
51
57
 
52
58
  export default {
53
59
  name: 'LancomCartShipping',
@@ -70,6 +76,7 @@ export default {
70
76
  computed: {
71
77
  ...mapGetters(['shop', 'SETTINGS', 'MESSAGES', 'country', 'currency', 'contacts']),
72
78
  ...mapGetters('cart', [
79
+ 'entities',
73
80
  'suburb',
74
81
  'notValidProductsPickup',
75
82
  'needToPickup',
@@ -87,7 +94,7 @@ export default {
87
94
  return this.onlyPostcode ? 'Postcode' : (this.MESSAGES.CART_POSTCODE || 'Postcode');
88
95
  },
89
96
  hasNotValidProductsPickup() {
90
- return this.notValidProductsPickup.length > 0;
97
+ return this.notValidProductsPickup.length > 0 && !this.suburb;
91
98
  },
92
99
  wantsToPickup: {
93
100
  get() {
@@ -95,6 +102,7 @@ export default {
95
102
  },
96
103
  set(needToPickup) {
97
104
  if (needToPickup && this.hasNotValidProductsPickup) {
105
+ this.setNeedToPickup(needToPickup);
98
106
  this.visiblePickupError = true;
99
107
  } else {
100
108
  // if (needToPickup) {
@@ -104,21 +112,48 @@ export default {
104
112
  this.visiblePickupError = false;
105
113
  }
106
114
  }
115
+ },
116
+ notValidProductsPickupBySKU() {
117
+ return groupProductsBySKU(this.notValidProductsPickup, 'productSKU');
118
+ },
119
+ isAvailableForPickupSomeProducts() {
120
+ const products = this.entities.map(({ product }) => product);
121
+ const grouped = groupProductsBySKU(products);
122
+ return this.notValidProductsPickupBySKU.length === 0 || this.notValidProductsPickupBySKU.length !== grouped?.length;
123
+ }
124
+ },
125
+ mounted() {
126
+ if (!this.isAvailableForPickupSomeProducts) {
127
+ this.wantsToPickup = false;
128
+ }
129
+ if (this.wantsToPickup && this.hasNotValidProductsPickup) {
130
+ this.visiblePickupError = true;
107
131
  }
108
132
  },
109
133
  methods: {
110
- ...mapMutations([
134
+ ...mapActions([
111
135
  'setSuburb',
136
+ 'calculateCartPrice',
112
137
  'setNeedToPickup'
113
138
  ]),
114
- ...mapActions([
115
- 'calculateCartPrice'
116
- ]),
117
139
  async calculatePrice() {
118
140
  await this.calculateCartPrice({ shop: this.shop, country: this.country, currency: this.currency });
119
141
  }
120
142
  }
121
143
  };
144
+
145
+ function groupProductsBySKU(products, skuProp = 'SKU') {
146
+ const grouped = products.reduce((acc, product) => {
147
+ const existingProduct = acc.find(p => p[skuProp] === product[skuProp]);
148
+ if (existingProduct) {
149
+ existingProduct.amount += product.amount;
150
+ } else {
151
+ acc.push({ ...product });
152
+ }
153
+ return acc;
154
+ }, []);
155
+ return grouped;
156
+ }
122
157
  </script>
123
158
 
124
159
  <style lang="scss" scoped>
@@ -218,7 +218,7 @@
218
218
  </template>
219
219
 
220
220
  <script>
221
- import { mapGetters, mapMutations, mapActions } from 'vuex';
221
+ import { mapGetters, mapActions } from 'vuex';
222
222
  import PostcodeSelect from '@lancom/shared/components/common/postcode_select/postcode-select';
223
223
  import PhoneInput from '@lancom/shared/components/common/phone_input/phone-input';
224
224
 
@@ -283,10 +283,8 @@ export default {
283
283
  validateAddress() {
284
284
  // this.$refs.addressProvider.validate();
285
285
  },
286
- ...mapMutations('cart', [
287
- 'setSuburb'
288
- ]),
289
286
  ...mapActions('cart', [
287
+ 'setSuburb',
290
288
  'calculateCartPrice'
291
289
  ]),
292
290
  setAddressSuburb(suburb) {
@@ -28,7 +28,7 @@
28
28
  <h5 class="lc_h3 lc_uppercase lc_bold mt-10 mb-15">Billing Address</h5>
29
29
  <address-form
30
30
  :address="order.billingAddress"
31
- :copy-from="order.shippingAddress"
31
+ :copy-from="needToPickupWithoutErrors ? null : order.shippingAddress"
32
32
  name-prefix="Delivery "
33
33
  :without-additional-info="true" />
34
34
  </div>
@@ -18,7 +18,7 @@
18
18
  </template>
19
19
 
20
20
  <script>
21
- import { mapMutations } from 'vuex';
21
+ import { mapActions } from 'vuex';
22
22
  import AddressForm from '@/components/checkout/order/address-form/address-form';
23
23
  import ProgressStepsControls from '@lancom/shared/components/common/progress_steps/progress_steps_controls/progress-steps-controls';
24
24
 
@@ -35,7 +35,7 @@ export default {
35
35
  }
36
36
  },
37
37
  methods: {
38
- ...mapMutations('cart', [
38
+ ...mapActions('cart', [
39
39
  'setSuburb'
40
40
  ]),
41
41
  copyFromBillingAddress() {
@@ -108,7 +108,7 @@ html[dir=rtl] {
108
108
  margin: 2px 0;
109
109
  &--disabled {
110
110
  pointer-events: none;
111
- opacity: .7;
111
+ opacity: .4;
112
112
  }
113
113
  }
114
114
 
@@ -173,11 +173,11 @@ export default {
173
173
  }
174
174
  }
175
175
  },
176
- mounted() {
176
+ async mounted() {
177
177
  if (this.suburb) {
178
178
  const option = this.createOptionFromSuburb(this.suburb);
179
179
  this.selected = option;
180
- this.options = [option];
180
+ await this.handleSearch(this.selected.value.trim())
181
181
  }
182
182
  },
183
183
  methods: {
@@ -241,7 +241,7 @@
241
241
  </template>
242
242
 
243
243
  <script>
244
- import { mapGetters, mapActions, mapMutations } from 'vuex';
244
+ import { mapGetters, mapActions } from 'vuex';
245
245
  import PostcodeSelect from '@lancom/shared/components/common/postcode_select/postcode-select';
246
246
  import PhoneInput from '@lancom/shared/components/common/phone_input/phone-input';
247
247
  import ProductTotalPricing from '@lancom/shared/components/common/products_total_pricing/products-total-pricing';
@@ -298,11 +298,9 @@ export default {
298
298
  ...mapActions('order', [
299
299
  'createOrder'
300
300
  ]),
301
- ...mapMutations('cart', [
302
- 'setSuburb'
303
- ]),
304
301
  ...mapActions('cart', [
305
- 'calculateCartPrice'
302
+ 'calculateCartPrice',
303
+ 'setSuburb'
306
304
  ]),
307
305
  setAddressSuburb(suburb) {
308
306
  this.$set(this.form, 'suburb', suburb);
@@ -2,12 +2,12 @@
2
2
  <div
3
3
  class="ProductPriceRange__wrapper">
4
4
  <div
5
- v-if="product[minPriceAttr] === product[maxPriceAttr]"
5
+ v-if="product[minPriceAttr] === product[maxPriceAttr] || !product[maxPriceAttr]"
6
6
  class="ProductPriceRange__price lc_h4">
7
7
  <div
8
8
  v-if="withGst"
9
9
  class="lc_h4">
10
- {{ product[minPriceAttr] | tax(gstTax) | price(currency) }}
10
+ {{ prePriceText }} {{ product[minPriceAttr] | tax(gstTax) | price(currency) }}
11
11
  <span
12
12
  v-if="visiblePriceInfo"
13
13
  class="lc_regular11 lc_bold">inc. {{ taxName }}</span>
@@ -51,4 +51,9 @@
51
51
  background-color: $gray;
52
52
  padding: 15px;
53
53
  }
54
+ &__message {
55
+ color: $error;
56
+ font-size: 12px;
57
+ margin-bottom: 5px;
58
+ }
54
59
  }
@@ -1,29 +1,23 @@
1
1
  <template>
2
2
  <div>
3
- <article class="ProductsKit__wrapper">
4
- <div class="ProductsKit__header-info post-full-content">
5
- <div class="post-content">
6
- <h1 class="lc_h1">
7
- {{ productsKit.name }}
8
- </h1>
9
- </div>
10
- </div>
11
- <div class="ProductsKit__post post-full-content">
12
- <div
13
- class="lc_regular14"
14
- v-html="productsKit.description"></div>
15
- </div>
16
- </article>
17
3
  <products-kit-options
4
+ v-if="productsKit[optionsAttr]"
18
5
  class="ProductsKit__options"
19
- :products-kit="productsKit" />
20
- <products-kit-cart
21
- class="ProductsKit__cart"
22
- :products-kit="productsKit" />
6
+ :products-kit="productsKit"
7
+ :options-attr="optionsAttr" />
8
+ <div class="ProductsKit__cart">
9
+ <div
10
+ v-if="!isAllOptionsSelected"
11
+ class="ProductsKit__message">
12
+ Please choose one product of each required option
13
+ </div>
14
+ <products-kit-cart :products-kit="productsKit" />
15
+ </div>
23
16
  </div>
24
17
  </template>
25
18
 
26
19
  <script>
20
+ import { mapGetters } from 'vuex';
27
21
  import ProductsKitOptions from './products_kit_options/products-kit-options';
28
22
  import ProductsKitCart from './products_kit_cart/products-kit-cart';
29
23
 
@@ -37,7 +31,14 @@ export default {
37
31
  productsKit: {
38
32
  type: Object,
39
33
  required: true
34
+ },
35
+ optionsAttr: {
36
+ type: String,
37
+ default: 'productsKitOptions'
40
38
  }
39
+ },
40
+ computed: {
41
+ ...mapGetters('productsKit', ['isAllOptionsSelected'])
41
42
  }
42
43
  };
43
44
  </script>
@@ -22,7 +22,7 @@
22
22
  input {
23
23
  padding: 14px;
24
24
  font-size: 20px;
25
- width: 150px;
25
+ width: 110px;
26
26
  }
27
27
  .NumberInput__side-control {
28
28
  width: 30px !important;
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="ProductsKitCartOptions__wrapper">
3
3
  <products-kit-cart-option
4
- v-for="option in productsKit.options"
4
+ v-for="option in productsKit.productsKitOptions"
5
5
  :key="option._id"
6
6
  :products-kit="productsKit"
7
7
  :option="option"
@@ -37,5 +37,11 @@
37
37
  font-weight: bold;
38
38
  color: grey;
39
39
  }
40
+ &--alert {
41
+ background-color: rgb(255, 201, 201);
42
+ span {
43
+ color: rgb(137, 38, 38);
44
+ }
45
+ }
40
46
  }
41
47
  }
@@ -14,8 +14,15 @@
14
14
  <div
15
15
  v-else
16
16
  class="ProductsKitCartOption__placeholder"
17
+ :class="{
18
+ 'ProductsKitCartOption__placeholder--alert': option.required
19
+ }"
17
20
  @click="scrollToOption()">
18
- <span>+</span>
21
+ <i
22
+ v-if="option.required"
23
+ class="icon-attention-circled"
24
+ v-tooltip="'Please choose product'"></i>
25
+ <span v-else>+</span>
19
26
  </div>
20
27
  </div>
21
28
  </template>
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="ProductsKitOptions__wrapper">
3
3
  <products-kit-option
4
- v-for="option in productsKit.options"
4
+ v-for="option in productsKit[optionsAttr]"
5
5
  :key="option._id"
6
6
  :products-kit="productsKit"
7
7
  :option="option"
@@ -22,6 +22,10 @@ export default {
22
22
  productsKit: {
23
23
  type: Object,
24
24
  required: true
25
+ },
26
+ optionsAttr: {
27
+ type: String,
28
+ default: 'productsKitOptions'
25
29
  }
26
30
  }
27
31
  };
@@ -79,6 +79,11 @@ export default {
79
79
  },
80
80
  methods: {
81
81
  ...mapActions('productsKit', ['selectOptionColor'])
82
+ },
83
+ mounted() {
84
+ if (!this.selectedColor && this.availableColors.length === 1) {
85
+ this.selectedColor = this.availableColors[0];
86
+ }
82
87
  }
83
88
  };
84
89
  </script>
@@ -76,6 +76,13 @@ export default {
76
76
  return Array.from(uniqueSizesMap.values());
77
77
  }
78
78
  },
79
+ watch: {
80
+ selectedColor() {
81
+ if (!this.selectedSize && this.availableSizes.length === 1) {
82
+ this.selectedSize = this.availableSizes[0];
83
+ }
84
+ }
85
+ },
79
86
  methods: {
80
87
  ...mapActions('productsKit', ['selectOptionSize'])
81
88
  }
@@ -36,6 +36,8 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
36
36
 
37
37
  const product = store.getters['product/product'];
38
38
 
39
+ await store.dispatch('productsKit/setProductsKit', product);
40
+
39
41
  if ((product.useTaxonomyUrl && !params.sku) || (!product.useTaxonomyUrl && params.sku)) {
40
42
  const redirectUrl = generateProductLink(product, data.defaultColor, isEditor);
41
43
  return redirect(301, redirectUrl);
@@ -91,6 +93,9 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
91
93
  computed: {
92
94
  ...mapGetters(['shop', 'gstTax', 'country', 'stockCountry', 'currency']),
93
95
  ...mapGetters('product', ['product', 'simpleProducts', 'productDetails', 'editableColor', 'images', 'preSetPrints', 'editorSize', 'printsPrice']),
96
+ hasProductsKitOptions() {
97
+ return this.product.productsKitOptions?.length > 0;
98
+ },
94
99
  stockCountryId() {
95
100
  return this.stockCountry?._id || null;
96
101
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.386",
3
+ "version": "0.0.388",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
@@ -9,7 +9,11 @@
9
9
  btn-class="black"
10
10
  btn-label="EDIT CART"
11
11
  :btn-block="true"
12
- to="/checkout/cart" />
12
+ to="/checkout/cart">
13
+ <template #icon-before>
14
+ <i class="icon-edit"></i>
15
+ </template>
16
+ </btn>
13
17
  <cart-price-info
14
18
  class="mt-10"
15
19
  :disabled-rates="true"
package/routes/index.js CHANGED
@@ -169,16 +169,6 @@ module.exports = function(routes, resolve) {
169
169
  path: '/news/tag/:tag',
170
170
  component: resolve('@lancom/shared/pages/news/tag/_tag.vue'),
171
171
  chunkName: 'pages/news/tag'
172
- }, {
173
- name: 'products-kits',
174
- path: '/products-kits',
175
- component: resolve('@lancom/shared/pages/products-kit/index.vue'),
176
- chunkName: 'pages/products-kits'
177
- }, {
178
- name: 'products-kit-view',
179
- path: '/products-kit/:alias',
180
- component: resolve('@lancom/shared/pages/products-kit/_alias.vue'),
181
- chunkName: 'pages/products-kit/products-kit-view'
182
172
  }, {
183
173
  name: 'contact',
184
174
  path: '/contact',
package/store/cart.js CHANGED
@@ -28,7 +28,7 @@ const getEntitiesQuantity = entities => {
28
28
  const getProductsQuantities = entities => {
29
29
  const grouped = groupBy(entities, e => e.product._id);
30
30
  const quantities = Object.keys(grouped).map(_id => {
31
- const [{ prints, product: { name, minimumOrderQuantity, minimumPrintOrderQuantity } }] = grouped[_id];
31
+ const [{ prints, product: { name, minimumOrderQuantity, minimumPrintOrderQuantity, SKU } }] = grouped[_id];
32
32
  const minQty = (prints?.length > 0 ? minimumPrintOrderQuantity : minimumOrderQuantity) || minimumOrderQuantity;
33
33
  const simpleProducts = grouped[_id].reduce((simpleProducts, product) => {
34
34
  product.simpleProducts?.forEach(simpleProduct => {
@@ -43,7 +43,7 @@ const getProductsQuantities = entities => {
43
43
  });
44
44
  return simpleProducts;
45
45
  }, []);
46
- return { _id, name, minimumOrderQuantity: minQty, quantity: getEntitiesQuantity(grouped[_id]), simpleProducts };
46
+ return { _id, name, SKU, minimumOrderQuantity: minQty, quantity: getEntitiesQuantity(grouped[_id]), simpleProducts };
47
47
  });
48
48
  return quantities;
49
49
  };
@@ -101,6 +101,7 @@ export const getters = {
101
101
  ...simpleProducts,
102
102
  ...product?.simpleProducts?.map(sp => ({
103
103
  name: product.name,
104
+ productSKU: product.SKU,
104
105
  ...sp
105
106
  }))
106
107
  ]
@@ -109,7 +110,6 @@ export const getters = {
109
110
  const notValid = simpleProducts.filter(sp => sp.warehouseQuantityStock < sp.amount)
110
111
 
111
112
  return notValid;
112
-
113
113
  },
114
114
  notValidStockQuantities(state, { quantities }) {
115
115
  return (quantities?.stock || []).filter(({ maxQuantity, quantity }) => quantity > maxQuantity);
@@ -190,7 +190,7 @@ export const actions = {
190
190
  await api.saveCart(payload, shop._id);
191
191
  commit('setEntities', entities);
192
192
  },
193
- async calculateCartPrice({ state: { suburb, entities, coupon, cartPricing }, getters: { needToPickupWithoutErrors }, commit }, { shop, country, currency }) {
193
+ async calculateCartPrice({ state: { suburb, entities, coupon, cartPricing }, getters: { needToPickup }, commit }, { shop, country, currency }) {
194
194
  let savedSuppliersWithRates = null;
195
195
  try {
196
196
  savedSuppliersWithRates = JSON.parse(localStorage.getItem(SUPPLIERS_WITH_RATES_KEY));
@@ -200,7 +200,7 @@ export const actions = {
200
200
  console.log('calculateCartPrice:payload: ', payload)
201
201
  try {
202
202
  commit('setCartPricingCalculating', true);
203
- const response = await api.calculateProductPrice({ ...payload, needToPickup: needToPickupWithoutErrors }, shop._id);
203
+ const response = await api.calculateProductPrice({ ...payload, needToPickup }, shop._id);
204
204
  commit('setCartPricing', response);
205
205
  commit('setCartPricingError', null);
206
206
  } catch (e) {
@@ -226,6 +226,17 @@ export const actions = {
226
226
  clearCart({ commit }) {
227
227
  commit('clearCart');
228
228
  },
229
+ setNeedToPickup({ commit }, needToPickup) {
230
+ localStorage.removeItem(SUPPLIERS_WITH_RATES_KEY);
231
+ commit('setNeedToPickup', needToPickup);
232
+ commit('setCartPricing', null);
233
+ },
234
+ setSuburb({ commit }, suburb) {
235
+ localStorage.removeItem(SUPPLIERS_WITH_RATES_KEY);
236
+ commit('setSuburb', suburb);
237
+ commit('setCartPricing', null);
238
+
239
+ },
229
240
  async selectRate({ state: { cartPricing, entities, suburb, coupon }, commit }, { supplier, rate, shop, country, currency }) {
230
241
  const suppliersWithRates = cartPricing.shipping.suppliersWithRates
231
242
  .map(supplierWithRates => ({
@@ -41,7 +41,8 @@ export const getters = {
41
41
  selectedOptionsSizes: ({ selectedOptionsSizes }) => selectedOptionsSizes,
42
42
  isAllOptionsSelected: ({ productsKit, selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes }) => {
43
43
  const selectedSimpleProducts = getSelectedSimpleProducts(selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes);
44
- return productsKit.options?.length === selectedSimpleProducts?.length;
44
+ const requiredOptions = productsKit.productsKitOptions?.filter(pk => pk.required) || [];
45
+ return requiredOptions?.length <= selectedSimpleProducts?.length;
45
46
  },
46
47
  selectedSimpleProducts: ({ selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes }) => getSelectedSimpleProducts(selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes),
47
48
  maxOrderQty: ({ selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes }) => {
@@ -53,7 +54,7 @@ export const getters = {
53
54
  },
54
55
  cartEntities: ({ amount, productsKit, selectedOptionsProducts, selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes }) => {
55
56
  const productsKitGuid = generateGUID();
56
- const entities = productsKit.options
57
+ const entities = productsKit.productsKitOptions
57
58
  .map(option => {
58
59
  const product = selectedOptionsProducts[option._id];
59
60
  if (product) {
@@ -87,6 +88,9 @@ export const actions = {
87
88
  });
88
89
  }
89
90
  },
91
+ setProductsKit({ commit }, productsKit) {
92
+ commit('setProductsKit', productsKit);
93
+ },
90
94
  async selectOptionProduct({ commit }, { option, product, country, currency, stockCountry }) {
91
95
  const params = { country, currency, stockCountry };
92
96
  const simpleProducts = await api.fetchProductDetails(null, product.alias, params);
@@ -104,7 +108,7 @@ export const actions = {
104
108
  },
105
109
  clearOptions({ commit, state }) {
106
110
  const { productsKit } = state;
107
- productsKit.options?.forEach(option => {
111
+ productsKit.productsKitOptions?.forEach(option => {
108
112
  commit('clearOption', option);
109
113
  });
110
114
  },
@@ -117,7 +121,7 @@ export const actions = {
117
121
  async calculateProductsKitPrice({ state, commit }, { shop, country, currency }) {
118
122
  commit('setCalculatingPrice', true);
119
123
  const { amount, selectedOptionsProducts, productsKit, selectedOptionsSimpleProducts, selectedOptionsColors, selectedOptionsSizes } = state;
120
- const entities = productsKit.options
124
+ const entities = productsKit.productsKitOptions
121
125
  .map(option => {
122
126
  const product = selectedOptionsProducts[option._id];
123
127
  if (product) {
@@ -1,103 +0,0 @@
1
- <template>
2
- <div class="ProductsKitPage__wrapper">
3
- <breadcrumbs :items="breadcrumbs" />
4
- <transition name="from-right-to-left" appear>
5
- <products-kit :products-kit="productsKit" />
6
- </transition>
7
- <transition
8
- v-if="hasProductsKitsList"
9
- name="from-left-to-right"
10
- appear>
11
- <div class="content-inner">
12
- <h2 class="lc_h2 lc_black ProductsKitPage__list-title">
13
- You might also like
14
- </h2>
15
- <products-kits-list
16
- class="ProductsKitPage__list"
17
- :productsKits="productsKitsList" />
18
- </div>
19
- </transition>
20
- </div>
21
- </template>
22
- <script>
23
- import metaInfo from '@lancom/shared/mixins/meta-info';
24
-
25
- export default {
26
- name: 'ProductsKitPage',
27
- components: {
28
- Breadcrumbs: () => import('@lancom/shared/components/common/breadcrumbs/breadcrumbs'),
29
- ProductsKit: () => import('@lancom/shared/components/products_kit/products_kit/products-kit'),
30
- ProductsKitsList: () => import('@lancom/shared/components/products_kit/products_kits_list/products-kits-list')
31
- },
32
- mixins: [metaInfo],
33
- async asyncData({ params, store, error, redirect }) {
34
- console.log('asyncData: ', 1);
35
- try {
36
- const { shop, country, currency } = store.getters;
37
- const data = {
38
- shop: shop._id,
39
- alias: params.alias,
40
- country: country?._id,
41
- currency: currency?._id
42
- };
43
- await store.dispatch('productsKit/fetchProductsKit', data);
44
- } catch (e) {
45
- console.log(e);
46
- }
47
- const productsKit = store.getters['productsKit/productsKit'];
48
- const loadError = store.getters['productsKit/loadError'];
49
-
50
- // console.log('loadError: ', loadError);
51
- if (loadError?.redirectLink) {
52
- return redirect(301, loadError?.redirectLink);
53
- } else if (loadError) {
54
- error(loadError);
55
- }
56
- return {
57
- productsKit
58
- };
59
- },
60
- data() {
61
- return {
62
- productsKit: null,
63
- productsKits: []
64
- };
65
- },
66
- computed: {
67
- hasProductsKitsList() {
68
- return this.productsKitsList.length > 0;
69
- },
70
- productsKitsList() {
71
- return this.productsKits.filter(i => i.alias !== this.productsKit.alias).slice(0, 3);
72
- },
73
- breadcrumbs() {
74
- const item = this.productsKit ? { text: this.productsKit.name } : null;
75
- return [{
76
- to: '/products-kits',
77
- text: 'Products Kits'
78
- }, item].filter(i => !!i);
79
- },
80
- pageItemType() {
81
- return 'products-kits';
82
- }
83
- },
84
- getRoute() {
85
- return '/shop/:shop/products-kits/:alias';
86
- }
87
- };
88
- </script>
89
-
90
- <style lang="scss" scoped>
91
- .ProductsKitPage{
92
- &__wrapper {
93
- margin-top: 30px;
94
- }
95
- &__list-title {
96
- margin-top: 20px;
97
- text-align: center;
98
- }
99
- &__list {
100
- padding: 20px 0;
101
- }
102
- }
103
- </style>
@@ -1,59 +0,0 @@
1
- <template>
2
- <div class="ProductsKitsPage__wrapper view-transition">
3
- <div class="content-inner">
4
- <breadcrumbs :items="breadcrumbs" />
5
- <products-kits-list
6
- class="ProductsKitsPage__list"
7
- :products-kits="productsKits"
8
- :count="count"
9
- :page="page"
10
- :per-page="perPage" />
11
- </div>
12
- </div>
13
- </template>
14
-
15
- <script>
16
- import metaInfo from '@lancom/shared/mixins/meta-info';
17
- import api from '@lancom/shared/assets/js/api';
18
-
19
- const KITS_PER_PAGE = 18;
20
-
21
- export default {
22
- name: 'ProductPage',
23
- components: {
24
- Breadcrumbs: () => import('@lancom/shared/components/common/breadcrumbs/breadcrumbs'),
25
- ProductsKitsList: () => import('@lancom/shared/components/products_kit/products_kits_list/products-kits-list')
26
- },
27
- mixins: [metaInfo],
28
- async asyncData({ store, params, query }) {
29
- const { _id } = store.getters.shop;
30
- const kitsQuery = { ...params, ...query, limit: KITS_PER_PAGE };
31
- const { productsKits, count, page, perPage } = await api.fetchProductsKits(_id, kitsQuery);
32
- return { productsKits, count, page, perPage };
33
- },
34
- data() {
35
- return {
36
- productsKits: [],
37
- count: null,
38
- page: null,
39
- perPage: null,
40
- breadcrumbs: [{
41
- text: 'Products Kits'
42
- }]
43
- };
44
- },
45
- computed: {
46
- pageItemType() {
47
- return 'products-kits';
48
- }
49
- }
50
- };
51
- </script>
52
-
53
- <style lang="scss" scoped>
54
- .ProductsKitsPage {
55
- &__wrapper {
56
- padding: 30px 0;
57
- }
58
- }
59
- </style>