@lancom/shared 0.0.264 → 0.0.265

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.
@@ -284,6 +284,9 @@ export default {
284
284
  fetchSimpleProducts(params) {
285
285
  return _get('admin/simple-products', params);
286
286
  },
287
+ fetchProductSimpleProducts(alias, params) {
288
+ return _get(`admin/products/${alias}/simple-products`, params);
289
+ },
287
290
  clearCanonicalProductFromSimpleProduct(product, simpleProduct) {
288
291
  return _delete(`admin/products/${product}/simple-products/${simpleProduct}/canonical`);
289
292
  },
@@ -69,7 +69,7 @@ export default {
69
69
  sizesRange
70
70
  },
71
71
  computed: {
72
- ...mapGetters(['displayPricingWithTax', 'pricingSettings']),
72
+ ...mapGetters(['displayPricingWithTax', 'pricingSettings', 'currency']),
73
73
  ...mapGetters('product', [
74
74
  'product',
75
75
  'usedSimpleProducts',
@@ -0,0 +1,60 @@
1
+ @import "@/assets/scss/variables";
2
+
3
+ .ProductMultipacksCarousel {
4
+ &__wrapper {
5
+ width: 100%;
6
+ position: relative;
7
+ z-index: 1;
8
+ overflow-x: hidden;
9
+ display: block;
10
+ // align-items: center;
11
+
12
+ ::v-deep .slick-dots {
13
+ display: none !important;
14
+ }
15
+ ::v-deep .slick-list {
16
+ min-height: 205px;
17
+ }
18
+ ::v-deep .slick-arrow.slick-prev,
19
+ ::v-deep .slick-arrow.slick-next {
20
+ position: absolute;
21
+ bottom: 25px;
22
+ right: 0;
23
+ background-color: black;
24
+ border: 1px solid white;
25
+ background-image: url(./../../../static/icons/arrow-left.svg);
26
+ background-position: center;
27
+ background-repeat: no-repeat;
28
+ background-size: 15px 15px;
29
+ width: 41px;
30
+ height: 41px;
31
+ text-indent: -9999px;
32
+ cursor: pointer;
33
+ z-index: 2;
34
+
35
+ &:hover {
36
+ box-shadow: 0 0 3px $grey_2;
37
+ }
38
+ }
39
+ ::v-deep .slick-arrow.slick-prev {
40
+ transform: rotateZ(180deg);
41
+ left: 0;
42
+ right: auto;
43
+ }
44
+ ::v-deep .slick-arrow.slick-prev:focus,
45
+ ::v-deep .slick-arrow.slick-next:focus {
46
+ outline: none;
47
+ }
48
+ }
49
+ &__items {
50
+ display: flex;
51
+ justify-content: center;
52
+ }
53
+ &__item {
54
+ padding: 10px;
55
+ width: 180px;
56
+ height: 200px;
57
+ max-width: 300px;
58
+ flex-grow: 1;
59
+ }
60
+ }
@@ -0,0 +1,80 @@
1
+ <template>
2
+ <div class="ProductMultipacksCarousel__wrapper">
3
+ <div class="ProductMultipacksCarousel__items">
4
+ <div
5
+ v-for="multipack in visibleMultipacks"
6
+ :key="multipack._id"
7
+ class="ProductMultipacksCarousel__item">
8
+ <product-multipack
9
+ :multipack="multipack"
10
+ :simple-products="simpleProducts"
11
+ class="elevation2" />
12
+ </div>
13
+ </div>
14
+ <button
15
+ type="button"
16
+ class="slick-arrow slick-prev slick-disabled"
17
+ @click="goToPrevPage()">
18
+ Previous
19
+ </button>
20
+ <button
21
+ type="button"
22
+ class="slick-arrow slick-next"
23
+ @click="goToNextPage()">
24
+ Next
25
+ </button>
26
+ </div>
27
+ </template>
28
+
29
+ <script>
30
+ import ProductMultipack from './product_multipack/product-multipack';
31
+
32
+ export default {
33
+ name: 'LancomProductMultipacks',
34
+ components: {
35
+ ProductMultipack
36
+ },
37
+ data() {
38
+ return {
39
+ thumbsPage: 0
40
+ };
41
+ },
42
+ props: {
43
+ product: {
44
+ type: Array,
45
+ required: true
46
+ },
47
+ simpleProducts: {
48
+ type: Array,
49
+ required: true
50
+ },
51
+ slidesPerRow: {
52
+ type: Number,
53
+ default: 3
54
+ }
55
+ },
56
+ computed: {
57
+ multipacks() {
58
+ return this.product.multipacks || [];
59
+ },
60
+ visibleFrom() {
61
+ return this.thumbsPage * this.slidesPerRow;
62
+ },
63
+ visibleMultipacks() {
64
+ return this.multipacks.slice(this.visibleFrom, this.visibleFrom + this.slidesPerRow);
65
+ }
66
+ },
67
+ methods: {
68
+ goToPrevPage() {
69
+ this.thumbsPage = Math.max(0, this.thumbsPage - 1);
70
+ },
71
+ goToNextPage() {
72
+ this.thumbsPage = Math.min(Math.ceil((this.multipacks.length) / this.slidesPerRow) - 1, this.thumbsPage + 1);
73
+ }
74
+ }
75
+ };
76
+ </script>
77
+
78
+ <style lang="scss" scoped>
79
+ @import 'product-multipacks-carousel';
80
+ </style>
@@ -0,0 +1,43 @@
1
+ @import "@/assets/scss/variables";
2
+
3
+ .ProductMultipack {
4
+ &__price {
5
+ padding: 15px;
6
+ text-align: center;
7
+ font-size: 14px;
8
+ font-weight: bold;
9
+ background-color: $gray;
10
+ }
11
+ &__image {
12
+ position: absolute;
13
+ left: 0;
14
+ top: 0;
15
+ right: 0;
16
+ bottom: 0;
17
+ background-position: left top;
18
+ background-repeat: no-repeat;
19
+ background-size: contain;
20
+ }
21
+ &__banner {
22
+ position: relative;
23
+ height: 140px;
24
+ &-info {
25
+ position: absolute;
26
+ top: 20px;
27
+ right: 10px;
28
+ display: flex;
29
+ flex-direction: column;
30
+ align-items: end;
31
+ &-item span {
32
+ margin-bottom: 10px;
33
+ text-align: center;
34
+ padding: 6px;
35
+ background-color: $orange;
36
+ color: $white;
37
+ display: inline-block;
38
+ font-weight: bold;
39
+ font-size: 15px;
40
+ }
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,81 @@
1
+ <template>
2
+ <div
3
+ :id="`product-multipack-${multipack._id}`"
4
+ class="ProductMultipack__wrapper">
5
+ <div class="ProductMultipack__banner">
6
+ <div
7
+ class="ProductMultipack__image"
8
+ :style="{
9
+ 'background-image': multipack.image ? `url('${multipack.image.small}'` : null
10
+ }">
11
+ </div>
12
+ <div class="ProductMultipack__banner-info">
13
+ <div class="ProductMultipack__banner-info-item">
14
+ <btn
15
+ style="padding: 10px; height: 35px; margin-bottom: 10px;"
16
+ btn-class="green"
17
+ btn-label="BUY"
18
+ size="sm"
19
+ @onclick="buyMultipack()" />
20
+ </div>
21
+ <div class="ProductMultipack__banner-info-item">
22
+ <span>{{ multipack.qty }}</span>
23
+ </div>
24
+ <div class="ProductMultipack__banner-info-item">
25
+ <span>{{ totalPrice | priceWithTax(pricingSettings, currency) }}</span>
26
+ </div>
27
+ </div>
28
+ </div>
29
+ <div class="ProductMultipack__price">
30
+ Only {{ oneItemPrice | priceWithTax(pricingSettings, currency) }} each
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <script>
36
+ import { mapGetters, mapMutations } from 'vuex';
37
+ import { priceWithTax, inRange } from '@lancom/shared/assets/js/utils/filters';
38
+
39
+ export default {
40
+ name: 'LancomProductMultipack',
41
+ filters: {
42
+ priceWithTax
43
+ },
44
+ props: {
45
+ multipack: {
46
+ type: Object,
47
+ required: true
48
+ },
49
+ simpleProducts: {
50
+ type: Array,
51
+ required: true
52
+ }
53
+ },
54
+ computed: {
55
+ ...mapGetters(['pricingSettings', 'currency']),
56
+ simpleProduct() {
57
+ return (this.simpleProducts || []).find(sp => sp.SKU === this.multipack.SKU);
58
+ },
59
+ totalPrice() {
60
+ return this.oneItemPrice * this.multipack.qty;
61
+ },
62
+ oneItemPrice() {
63
+ return (this.simpleProduct?.pricing || []).find(p => p.min === this.multipack.qty)?.price || 0;
64
+ }
65
+ },
66
+ methods: {
67
+ ...mapMutations('product', ['setSimpleProductAmount']),
68
+ buyMultipack() {
69
+ this.setSimpleProductAmount({
70
+ colorId: this.simpleProduct.color._id,
71
+ sizeId: this.simpleProduct.size._id,
72
+ amount: inRange(this.multipack.qty, 0, this.simpleProduct.quantityStock || 999)
73
+ });
74
+ }
75
+ }
76
+ };
77
+ </script>
78
+
79
+ <style lang="scss" scoped>
80
+ @import 'product-multipack.scss';
81
+ </style>
@@ -35,7 +35,7 @@ async function googleShoppingFeed(axios, config, availableStores) {
35
35
  const frontImages = (product.images || []).filter(i => (i.types || []).includes('front') && sp.color._id === i.color).map(i => i.image);
36
36
  const backImages = (product.images || []).filter(i => (i.types || []).includes('back') && sp.color._id === i.color).map(i => i.image);
37
37
  const catalogFrontImages = (product.images || []).filter(i => (i.types || []).includes('catalog_front')).map(i => i.image);
38
- const image = spliceFirstImage(feedImages) || spliceFirstImage(frontImages) || spliceFirstImage(catalogFrontImages) || spliceFirstImage(backImages) || {};
38
+ const image = spliceFirstImage(feedImages) || spliceFirstImage(frontImages) || spliceFirstImage(catalogFrontImages) || spliceFirstImage(backImages) || null;
39
39
  const images = getImages(backImages) || getImages(frontImages) || [];
40
40
  const feedTitle = (product.feedTitle || '')
41
41
  .replace(/{colour}/g, sp.color.name)
@@ -45,10 +45,15 @@ async function googleShoppingFeed(axios, config, availableStores) {
45
45
  const description = `${product.description || product.fabricInfoShort || product.name || ''}`
46
46
  .replace(/&nbsp;/g, ' ')
47
47
  .replace(/&middot;/, '·');
48
- const link = `https://${config.HOST_NAME}/${product.brand.alias}/${product.productType.alias}/${product.alias}?color=${sp.color.alias}`;
48
+ let link = `https://${config.HOST_NAME}/${product.brand.alias}/${product.productType.alias}/${product.alias}?color=${sp.color.alias}`;
49
+ if (sp.multipackQty) {
50
+ link = `${link}&multipack=${sp.SKU}`
51
+ }
52
+
53
+ const productWeight = +((product.weight || 0) * (sp.multipackQty || 1)).toFixed(3);
49
54
  const info = {
50
- title: { _text: title },
51
- description: { _text: description },
55
+ title: { _text: sp.title || title },
56
+ description: { _text: sp.description || description },
52
57
  link: { _text: link },
53
58
  'g:id': { _text: sp.SKU },
54
59
  'g:item_group_id': { _text: product.SKU },
@@ -61,16 +66,16 @@ async function googleShoppingFeed(axios, config, availableStores) {
61
66
  'g:condition': { _text: 'new' },
62
67
  'g:mpn': { _text: sp.SKU },
63
68
  'g:color': { _text: sp.color.name },
64
- 'g:image_link': { _text: image },
65
- 'g:additional_image_link': images.map(i => ({ _text: i })),
69
+ 'g:image_link': { _text: sp.image || image },
70
+ 'g:additional_image_link': (sp.image ? [image, ...images] : images).map(i => ({ _text: i })),
66
71
  'g:price': { _text: `${(sp.price || 0)} AUD` },
67
72
  'g:availability': { _text: sp.quantityStock > 0 ? 'in_stock' : 'out_of_stock' },
68
73
  'g:google_product_category': { _text: product.feedGoogleProductCategory || 2047 },
69
74
  'g:product_type': { _text: product.feedProductType || `Home > Products > ${product.productType.name}`, },
70
75
  'g:is_bundle': { _text: 'no' },
71
76
  'g:identifier_exists': sp.gtin ? 'yes' : 'no',
72
- 'g:product_weight': { _text: `${product.weight} kg` },
73
- 'g:shipping_weight': { _text: `${((product.weight || 0) + (product.weight || 0) * 0.05).toFixed(3)} kg` },
77
+ 'g:product_weight': { _text: `${productWeight} kg` },
78
+ 'g:shipping_weight': { _text: `${(productWeight + productWeight * 0.05).toFixed(3)} kg` },
74
79
  'g:quantity': { _text: sp.quantityStock },
75
80
  };
76
81
 
@@ -91,13 +96,13 @@ async function googleShoppingFeed(axios, config, availableStores) {
91
96
  }
92
97
  if (product.volume) {
93
98
  if (product.volume.length) {
94
- info['g:shipping_length'] = { _text: `${product.volume.length} cm` };
99
+ info['g:shipping_length'] = { _text: `${parseInt(product.volume.length * (sp.multipackQty || 1))} cm` };
95
100
  }
96
101
  if (product.volume.width) {
97
- info['g:shipping_width'] = { _text: `${product.volume.width} cm` };
102
+ info['g:shipping_width'] = { _text: `${parseInt(product.volume.width * (sp.multipackQty || 1))} cm` };
98
103
  }
99
104
  if (product.volume.height) {
100
- info['g:shipping_height'] = { _text: `${product.volume.height} cm` };
105
+ info['g:shipping_height'] = { _text: `${parseInt(product.volume.height * (sp.multipackQty || 1))} cm` };
101
106
  }
102
107
  }
103
108
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.264",
3
+ "version": "0.0.265",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {