@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.
- package/assets/scss/variables/_theme.scss +1 -0
- package/components/checkout/cart/cart.mixin.js +3 -3
- package/components/checkout/cart/cart_entity/cart-entity.mixin.js +8 -0
- package/components/checkout/cart/cart_entity/cart-entity.vue +10 -0
- package/components/checkout/cart/cart_products_kit_entity/cart-products-kit-entity.vue +4 -1
- package/components/checkout/cart/cart_shipping/cart-shipping.scss +5 -0
- package/components/checkout/cart/cart_shipping/cart-shipping.vue +45 -10
- package/components/checkout/order/address-form/address-form.vue +2 -4
- package/components/checkout/order/order-billing-information/order-billing-information.vue +1 -1
- package/components/checkout/order/order-shipping-information/order-shipping-information.vue +2 -2
- package/components/common/checkbox.vue +1 -1
- package/components/common/postcode_select/postcode-select.vue +2 -2
- package/components/modals/order_modal/order-modal.vue +3 -5
- package/components/product/product_price_range/product-price-range.vue +2 -2
- package/components/products_kit/products_kit/products-kit.scss +5 -0
- package/components/products_kit/products_kit/products-kit.vue +19 -18
- package/components/products_kit/products_kit/products_kit_cart/products-kit-cart.scss +1 -1
- package/components/products_kit/products_kit/products_kit_cart/products_kit_cart_options/products-kit-cart-options.vue +1 -1
- package/components/products_kit/products_kit/products_kit_cart/products_kit_cart_options/products_kit_cart_option/products-kit-cart-option.scss +6 -0
- package/components/products_kit/products_kit/products_kit_cart/products_kit_cart_options/products_kit_cart_option/products-kit-cart-option.vue +8 -1
- package/components/products_kit/products_kit/products_kit_options/products-kit-options.vue +5 -1
- package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_color/products-kit-option-color.vue +5 -0
- package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_size/products-kit-option-size.vue +7 -0
- package/mixins/product-view.js +5 -0
- package/package.json +1 -1
- package/pages/checkout/order.vue +5 -1
- package/routes/index.js +0 -10
- package/store/cart.js +16 -5
- package/store/productsKit.js +8 -4
- package/pages/products-kit/_alias.vue +0 -103
- package/pages/products-kit/index.vue +0 -59
|
@@ -35,7 +35,7 @@ export default {
|
|
|
35
35
|
return this.isNotValidProductsQuantity || this.isNotValidStockQuantity || this.isNotValidPrintsQuantity || this.isNotValidPrintsBigSizeQuantity;
|
|
36
36
|
},
|
|
37
37
|
isNotValidShipping() {
|
|
38
|
-
return
|
|
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
|
|
49
|
+
return generateProductLink(this.productsKitEntity.productsKit, null, !!this.SETTINGS.CART_PRODUCT_LINK_TO_EDITOR);
|
|
47
50
|
}
|
|
48
51
|
},
|
|
49
52
|
methods: {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div
|
|
3
3
|
class="CartShipping__wrapper"
|
|
4
4
|
:class="{
|
|
5
|
-
'CartShipping__wrapper--warning':
|
|
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
|
|
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
|
|
41
|
+
v-for="product in notValidProductsPickupBySKU"
|
|
36
42
|
:key="product._id">
|
|
37
|
-
<b>{{ product.name }}
|
|
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
|
|
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
|
-
...
|
|
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,
|
|
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 {
|
|
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
|
-
...
|
|
38
|
+
...mapActions('cart', [
|
|
39
39
|
'setSuburb'
|
|
40
40
|
]),
|
|
41
41
|
copyFromBillingAddress() {
|
|
@@ -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.
|
|
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
|
|
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>
|
|
@@ -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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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>
|
|
@@ -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
|
-
<
|
|
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
|
|
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
|
}
|
package/mixins/product-view.js
CHANGED
|
@@ -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
package/pages/checkout/order.vue
CHANGED
|
@@ -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: {
|
|
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
|
|
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 => ({
|
package/store/productsKit.js
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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>
|