@lancom/shared 0.0.350 → 0.0.352

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.
@@ -11,7 +11,7 @@ export const getColorImage = (product = {}, size = 'small', type, color, allowAn
11
11
  const validImages = (product.images || []).filter(i => !excludeTypes.some(type => isValidImageType(i, type)));
12
12
  const colorImage = color && validImages.find(i => isValidImageType(i, type || COLORS_IMAGES_TYPES.FRONT) && (color?._id === i.color || color?._id === i.color?._id));
13
13
  const image = colorImage || validImages.find(i => isValidImageType(i, type || COLORS_IMAGES_TYPES.FRONT) && (allowAnyColor || !i.color));
14
- return image && staticLink(image[size]);
14
+ return image && image[size] && staticLink(image[size]);
15
15
  };
16
16
 
17
17
  export const getProductCover = (product = {}, size = 'small', type = COLORS_IMAGES_TYPES.FRONT, color, allowAnyColor = false) => {
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <div>
3
+ <nav>
4
+ <ul class="pagination">
5
+ <li
6
+ v-for="page in totalPages"
7
+ :key="page"
8
+ :class="{ active: page === currentPage }">
9
+ <a :href="`${baseUrl}${page > 1 ? `${dividerPage}page=${page}` : ''}`">
10
+ {{ page }}
11
+ </a>
12
+ </li>
13
+ </ul>
14
+ </nav>
15
+ </div>
16
+ </template>
17
+
18
+ <script>
19
+ export default {
20
+ data() {
21
+ const baseUrl = this.$route.fullPath
22
+ .replace(/page=[0-9]+/, '')
23
+ .replace(/\?$/, '')
24
+ .replace(/\&$/, '');
25
+ return {
26
+ baseUrl,
27
+ dividerPage: baseUrl.includes('?') ? '&' : '?',
28
+ currentPage: parseInt(this.$route.query.page) || 1
29
+ };
30
+ },
31
+ props: {
32
+ itemsCount: {
33
+ type: Number,
34
+ required: true
35
+ },
36
+ itemsPerPage: {
37
+ type: Number,
38
+ default: 50
39
+ }
40
+ },
41
+ computed: {
42
+ totalPages() {
43
+ return Math.min(20, Math.ceil(this.itemsCount / this.itemsPerPage));
44
+ },
45
+ }
46
+ };
47
+ </script>
48
+
49
+ <style lang="scss" scoped>
50
+ @import "@/assets/scss/variables";
51
+ .pagination {
52
+ list-style: none;
53
+ display: flex;
54
+ gap: 6px;
55
+ padding: 0;
56
+ justify-content: center;
57
+ }
58
+
59
+ .pagination li {
60
+ cursor: pointer;
61
+ }
62
+
63
+ .pagination li.active {
64
+ background-color: $medium_gray;
65
+ color: white;
66
+ font-weight: bold;
67
+ }
68
+
69
+ .pagination li a {
70
+ text-decoration: none;
71
+ color: inherit;
72
+ display: block;
73
+ border: 1px solid #ddd;
74
+ padding: 8px 12px;
75
+ border-radius: 3px;
76
+ }
77
+ </style>
@@ -5,7 +5,7 @@
5
5
  name="header"
6
6
  :invalid="invalid">
7
7
  </slot>
8
- <div class="EditorLayerFormText__content">
8
+ <div class="EditorLayerFormText__content" @click.stop>
9
9
  <validation-provider
10
10
  tag="div"
11
11
  name="Copy"
@@ -13,6 +13,19 @@
13
13
  margin-top: -10px;
14
14
  }
15
15
  }
16
+ &__colors {
17
+ position: relative;
18
+ }
19
+ &__loader {
20
+ position: absolute;
21
+ top: 0;
22
+ right: 0;
23
+ bottom: 0;
24
+ left: 0;
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ }
16
29
  &__description {
17
30
  margin-top: 15px;
18
31
  color: rgb(118, 118, 118);
@@ -125,9 +125,17 @@
125
125
  <i class="icon-rotate-tee"></i>
126
126
  </div>
127
127
  </div>
128
+ <div class="EditorProductDetails__section">
129
+ <client-only>
130
+ <gallery
131
+ v-if="hasImages"
132
+ :show-big-image="false" />
133
+ </client-only>
134
+ </div>
128
135
  <div
129
136
  v-if="productDetailsLoaded"
130
- id="EditorProductDetails">
137
+ id="EditorProductDetails"
138
+ class="EditorProductDetails__colors">
131
139
  <div class="EditorProductDetails__section">
132
140
  <product-colors-selector
133
141
  :has-another-print-btn="false"
@@ -136,6 +144,13 @@
136
144
  <div class="EditorProductDetails__section">
137
145
  <editor-pricing :has-cart-btn="false" />
138
146
  </div>
147
+ <div
148
+ v-if="loadingProductDetails"
149
+ class="EditorProductDetails__loader">
150
+ <div>
151
+ <spinner />
152
+ </div>
153
+ </div>
139
154
  </div>
140
155
  </div>
141
156
  </template>
@@ -153,10 +168,12 @@ import Price from '@lancom/shared/components/common/price';
153
168
  import ProductSideWithPrint from '@lancom/shared/components/common/product_side_with_print/product-side-with-print';
154
169
  import RichText from '@lancom/shared/components/common/rich-text';
155
170
  import QuoteRequestModal from '@lancom/shared/components/quotes/quote_request_modal/quote-request-modal';
171
+ import Gallery from '@lancom/shared/components/product/gallery/gallery';
156
172
 
157
173
  export default {
158
174
  name: 'EditorProductDetails',
159
175
  components: {
176
+ Gallery,
160
177
  ProductColorsSelector,
161
178
  PricingDiscountsTable,
162
179
  EditorPricing,
@@ -179,6 +196,7 @@ export default {
179
196
  computed: {
180
197
  ...mapGetters(['taxName']),
181
198
  ...mapGetters('product', [
199
+ 'loadingProductDetails',
182
200
  'product',
183
201
  'editableColor',
184
202
  'images',
@@ -197,6 +215,9 @@ export default {
197
215
  'pricingSettings',
198
216
  'country'
199
217
  ]),
218
+ hasImages() {
219
+ return this.images.length > 0;
220
+ },
200
221
  previewPrintProduct() {
201
222
  return {
202
223
  ...this.product,
@@ -136,14 +136,7 @@ export default {
136
136
  size: null,
137
137
  fabricHelper: null,
138
138
  preloading: true,
139
- isRotating: false,
140
- productSides: [
141
- { label: 'Front', value: 'front' },
142
- { label: 'Back', value: 'back' }
143
- // { label: 'Left sleeve', value: 'left_sleeve' },
144
- // { label: 'Right sleeve', value: 'right_sleeve' },
145
- // { label: 'Outside label', value: 'outside_label' }
146
- ]
139
+ isRotating: false
147
140
  };
148
141
  },
149
142
  props: {
@@ -164,6 +157,14 @@ export default {
164
157
  'editableLayers',
165
158
  'editorSize'
166
159
  ]),
160
+ productSides() {
161
+ const sides = [
162
+ { label: 'Front', value: 'front' },
163
+ { label: 'Back', value: 'back' }
164
+ ]
165
+ const validSides = sides.filter(s => this.product.printAreas?.some(pa => pa.side === s.value));
166
+ return validSides[0] ? validSides : [sides[0]];
167
+ },
167
168
  sideZoomSize() {
168
169
  return this.printAreaZoomSize?.width;
169
170
  },
@@ -88,6 +88,9 @@
88
88
  display: inline;
89
89
  }
90
90
  }
91
+ &:last-child {
92
+ display: none;
93
+ }
91
94
  }
92
95
  &-divider {
93
96
  color: $grey_1;
@@ -110,10 +113,17 @@
110
113
  }
111
114
  }
112
115
  &.side {
113
- width: 20px;
116
+ display: flex;
117
+ flex-direction: row;
114
118
  .EditorWorkspaceSide__placeholder-option span {
115
119
  &:last-child {
116
- display: inline;
120
+ display: inline-block;
121
+ background-color: $purple;
122
+ color: $white;
123
+ cursor: pointer;
124
+ padding: 4px;
125
+ margin: 1px;
126
+ font-size: 15px;
117
127
  }
118
128
  &:first-child {
119
129
  display: none;
@@ -122,6 +132,13 @@
122
132
  .EditorWorkspaceSide__placeholder-divider {
123
133
  display: none;
124
134
  }
135
+ .EditorWorkspaceSide__placeholder-option--edit-btn {
136
+ display: flex;
137
+ span {
138
+ margin-top: -0.5px !important;
139
+ display: inline-block !important;
140
+ }
141
+ }
125
142
  }
126
143
  }
127
144
  &__alert {
@@ -33,7 +33,7 @@
33
33
  </div>
34
34
  </div>
35
35
  <div
36
- v-if="fabricHelper"
36
+ v-if="fabricHelper && !editModeSelectedLayer"
37
37
  class="EditorWorkspaceSide__placeholder"
38
38
  :class="{
39
39
  tighten: !isZoomed && printAreaIsSmall,
@@ -45,7 +45,7 @@
45
45
  @click="createTextLayer"
46
46
  class="EditorWorkspaceSide__placeholder-option">
47
47
  <span>Type here</span>
48
- <span>T</span>
48
+ <span style="margin-top: -0.5px;">Text</span>
49
49
  </div>
50
50
  <div class="EditorWorkspaceSide__placeholder-divider">
51
51
  or
@@ -61,7 +61,7 @@
61
61
  <i class="icon-picture"></i> Add art
62
62
  </span>
63
63
  <span>
64
- <i class="icon-picture"></i>
64
+ Art
65
65
  </span>
66
66
  </div>
67
67
  </template>
@@ -77,6 +77,12 @@
77
77
  </template>
78
78
  </file-uploader>
79
79
  </div>
80
+ <!-- <div
81
+ v-if="editablePrintAreaLayers"
82
+ @click="editFirstLayer"
83
+ class="EditorWorkspaceSide__placeholder-option EditorWorkspaceSide__placeholder-option--edit-btn">
84
+ <span>Edit</span>
85
+ </div> -->
80
86
  </div>
81
87
  <div
82
88
  v-show="isVisibleOverlay"
@@ -180,6 +186,9 @@ export default {
180
186
  sideEditableLayers() {
181
187
  return this.editableLayers.filter(l => l.sideId === this.side);
182
188
  },
189
+ editablePrintAreaLayers() {
190
+ return this.printAreaLayers.some(l => l.editableDetails);
191
+ },
183
192
  printAreaLayers() {
184
193
  const paId = this.printArea?.parentPrintArea || this.printArea?._id;
185
194
  const layers = this.editableLayers.filter(l => !paId || (l.printArea === paId));
@@ -196,8 +205,8 @@ export default {
196
205
  // console.log('this.fabricHelper.printAreaRect: ', this.fabricHelper.printAreaRect);
197
206
  const ratio = this.calcWorkspaceSize() / this.editorSize.width;
198
207
  return this.printAreaLayers.length > 0 ? {
199
- top: `${top * ratio}px`,
200
- left: `${(left + width) * ratio + 4}px`
208
+ top: `${(top + height) * ratio}px`,
209
+ left: `${left * ratio}px`
201
210
  } : {
202
211
  top: `${top * ratio}px`,
203
212
  left: `${left * ratio}px`,
@@ -275,12 +284,20 @@ export default {
275
284
  ...mapActions('product', ['createLayer']),
276
285
  ...mapMutations('product', [
277
286
  'setSelectedLayerField',
287
+ 'setEditModeSelectedLayer',
278
288
  'setSelectedLayer',
279
289
  'removeTemplateLayer'
280
290
  ]),
281
291
  ...mapMutations('layers', [
282
292
  'setLayersThumbnail'
283
293
  ]),
294
+ editFirstLayer() {
295
+ const layer = this.printAreaLayers.find(l => l.editableDetails);
296
+ if (layer?.editableDetails) {
297
+ this.setSelectedLayer(layer);
298
+ this.setEditModeSelectedLayer(true);
299
+ }
300
+ },
284
301
  checkVisibleWireframe() {
285
302
  this.visibleWireframe = this.sideEditableLayers.length > 0;
286
303
  },
@@ -16,16 +16,14 @@
16
16
  </div>
17
17
  </div>
18
18
  <pagination
19
- v-if="perPage > 0"
20
- v-model="currentPage"
21
- :records="count"
22
- :per-page="perPage"
23
- :options="{ chunk: 3 }" />
19
+ v-if="count > perPage"
20
+ :itemsCount="count"
21
+ :itemsPerPage="perPage" />
24
22
  </div>
25
23
  </template>
26
24
 
27
25
  <script>
28
- import Pagination from 'vue-pagination-2';
26
+ import Pagination from '@lancom/shared/components/common/pagination';
29
27
  import NewsListItem from '../news_list_item/news-list-item';
30
28
 
31
29
  export default {
@@ -48,23 +46,6 @@ export default {
48
46
  page: {
49
47
  type: Number
50
48
  }
51
- },
52
- computed: {
53
- currentPage: {
54
- get() {
55
- return this.page;
56
- },
57
- set(page) {
58
- const params = [];
59
- if (page > 1) {
60
- params.push(`page=${page}`);
61
- }
62
- const link = `${this.$route.path}${params.length ? `?${params.join('&')}` : ''}`;
63
- window.location = link;
64
- // this.$router.push(link);
65
- // window.scrollTo(0, 0);
66
- }
67
- }
68
49
  }
69
50
  };
70
51
  </script>
@@ -215,5 +215,11 @@
215
215
  max-width: 800px;
216
216
  max-height: 800px;
217
217
  }
218
+ &.preview {
219
+ img {
220
+ max-width: 350px;
221
+ max-height: 350px;
222
+ }
223
+ }
218
224
  }
219
225
  }
@@ -1,6 +1,8 @@
1
1
  <template>
2
2
  <div class="Gallery__wrapper">
3
- <div class="Gallery__big">
3
+ <div
4
+ v-if="showBigImage"
5
+ class="Gallery__big">
4
6
  <div class="Gallery__big-image">
5
7
  <img
6
8
  :src="staticLink(currentImage.large)"
@@ -40,7 +42,10 @@
40
42
  </div>
41
43
  <div
42
44
  v-if="isZooming"
43
- class="Gallery__zoom-image">
45
+ class="Gallery__zoom-image"
46
+ :class="{
47
+ preview: !showBigImage
48
+ }">
44
49
  <img
45
50
  :src="staticLink(currentImage.large)"
46
51
  :style="zoomImageStyles"
@@ -54,7 +59,9 @@
54
59
  :class="{
55
60
  'Gallery__small-image--active': (visibleImagesFrom + index) === activeIndex
56
61
  }"
57
- @click="goToSlideAndChangeColor(visibleImagesFrom + index)">
62
+ @click="goToSlideAndChangeColor(visibleImagesFrom + index)"
63
+ @mouseenter="startPreview(visibleImagesFrom + index)"
64
+ @mouseleave="stopZoom">
58
65
  <img
59
66
  :src="staticLink(image.small)"
60
67
  :alt="product.name"
@@ -116,6 +123,10 @@ export default {
116
123
  slidesPerRow: {
117
124
  type: Number,
118
125
  default: 6
126
+ },
127
+ showBigImage: {
128
+ type: Boolean,
129
+ default: true
119
130
  }
120
131
  },
121
132
  data() {
@@ -199,6 +210,12 @@ export default {
199
210
  methods: {
200
211
  ...mapActions('product', ['selectColor']),
201
212
  staticLink,
213
+ startPreview(index) {
214
+ if (!this.isZoomin) {
215
+ this.startZoom();
216
+ this.goToSlideAndChangeColor(index);
217
+ }
218
+ },
202
219
  startZoom() {
203
220
  this.isZooming = true;
204
221
  },
@@ -29,7 +29,7 @@
29
29
  class="lc_regular16 ProductMainInfo__description-body"
30
30
  v-html="product.description || '&mdash;'">
31
31
  </p>
32
- <div class="ProductMainInfo__additional-info">
32
+ <!-- <div class="ProductMainInfo__additional-info">
33
33
  <span class="lc_medium16">
34
34
  &bull;
35
35
  Shoulder to shoulder tape
@@ -52,7 +52,7 @@
52
52
  </div>
53
53
  </template>
54
54
  </v-popover>
55
- </div>
55
+ </div> -->
56
56
  </div>
57
57
  </div>
58
58
  </template>
@@ -18,6 +18,17 @@
18
18
  <section class="Product__section">
19
19
  <product-main-info :product="product" />
20
20
  </section>
21
+ <section class="Product__section">
22
+ <btn
23
+ :btn-block="true"
24
+ :to="goToEditorLink"
25
+ btn-class="purple"
26
+ btn-label="DESIGN AND ORDER">
27
+ <i
28
+ slot="icon-after"
29
+ class="icon-arrow-right"></i>
30
+ </btn>
31
+ </section>
21
32
  <section class="Product__section">
22
33
  <wizard-editor />
23
34
  </section>
@@ -44,6 +55,9 @@
44
55
 
45
56
  <script>
46
57
  import { mapGetters } from 'vuex';
58
+ import RelatedProducts from '@lancom/shared/components/product/related_products/related-products';
59
+ import Gallery from '@lancom/shared/components/product/gallery/gallery';
60
+ import { generateProductLink } from '@lancom/shared/assets/js/utils/product';
47
61
  import ProductMainInfo from './layouts/product_main_info/product-main-info';
48
62
  import ProductColorsSelector from './product_colors_selector/product-colors-selector';
49
63
  import ProductTierPrices from './layouts/product_tier_prices/product-tier-prices';
@@ -51,8 +65,6 @@ import ProductFabricAndSizeInfo from './layouts/product_fabric_and_size_info/pro
51
65
  import ProductPackaging from './layouts/product_packaging/product-packaging';
52
66
  import ProductModelMeasurements from './layouts/product_model_measurements/product-model-measurements';
53
67
  import ProductSizeTable from './layouts/product_size_table/product-size-table';
54
- import RelatedProducts from '@lancom/shared/components/product/related_products/related-products';
55
- import Gallery from '@lancom/shared/components/product/gallery/gallery';
56
68
  import WizardEditor from './wizard-editor/wizard-editor.vue';
57
69
 
58
70
 
@@ -80,6 +92,9 @@ export default {
80
92
  'productDetails',
81
93
  'editableColor'
82
94
  ]),
95
+ goToEditorLink() {
96
+ return generateProductLink(this.product, null, true);
97
+ },
83
98
  hasImages() {
84
99
  return this.images.length > 0;
85
100
  }
@@ -6,7 +6,7 @@
6
6
  <div
7
7
  v-if="hasAdditionalColors"
8
8
  :style="{
9
- 'top': additionalColorHeight,
9
+ 'top': hasMainColor ? additionalColorHeight : 0,
10
10
  width: '100%',
11
11
  height: '100%',
12
12
  position: 'relative'
@@ -41,6 +41,9 @@ export default {
41
41
  }
42
42
  },
43
43
  computed: {
44
+ hasMainColor() {
45
+ return (!!this.color?.pattern || !!this.color?.rgb);
46
+ },
44
47
  pattern() {
45
48
  return this.color.pattern && staticLink(this.color.pattern);
46
49
  },
@@ -54,7 +57,9 @@ export default {
54
57
  return (this.color.additionalColors || []);
55
58
  },
56
59
  additionalColorHeight() {
57
- return `${100 / (this.additionalColors.length + 1)}%`;
60
+ const color = this.hasMainColor ? 1 : 0;
61
+ const height = 100 / (this.additionalColors.length + color);
62
+ return `${height}%`;
58
63
  },
59
64
  additionalColorsThumbs() {
60
65
  return (this.color.additionalColors || [])
@@ -10,17 +10,16 @@
10
10
  :placeholder="placeholder" />
11
11
  </slot>
12
12
  <pagination
13
- v-model="currentPage"
14
- :records="count"
15
- :per-page="perPage"
16
- :options="{ chunk: 3 }" />
13
+ v-if="count > perPage"
14
+ :itemsCount="count"
15
+ :itemsPerPage="perPage" />
17
16
  </div>
18
17
  </div>
19
18
  </template>
20
19
 
21
20
  <script>
22
21
  import { mapGetters } from 'vuex';
23
- import Pagination from 'vue-pagination-2';
22
+ import Pagination from '@lancom/shared/components/common/pagination';
24
23
  import { generateProductsLink } from '@lancom/shared/assets/js/utils/product';
25
24
  import ProductList from '../product_list/product-list';
26
25
 
@@ -72,6 +72,7 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
72
72
  'g:item_group_id': { _text: product.SKU },
73
73
  'g:size': { _text: sp.size.name },
74
74
  'g:size_system': COUNTRIES_SIZE_SYSTEMS[country] || country || 'AU',
75
+ 'g:ships_from_country': country || 'AU',
75
76
  'g:size_type': 'regular',
76
77
  'g:gender': { _text: product.gender },
77
78
  'g:material': { _text: product.fabricInfoShort },
@@ -119,6 +120,10 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
119
120
  }
120
121
  }
121
122
 
123
+ if (product.country) {
124
+ info['g:shipping_label'] = product.country;
125
+ }
126
+
122
127
  const productHighlight = `${product.feedProductHighlight || ''}`
123
128
  .trim()
124
129
  .split(/\n+/g)
@@ -61,7 +61,7 @@
61
61
  import gtm from '@lancom/shared/assets/js/utils/gtm';
62
62
  import metaInfo from '@lancom/shared/mixins/meta-info';
63
63
  import { generateProductsLink, generateProductLink } from '@lancom/shared/assets/js/utils/product';
64
- import { getProductLargeCover } from '@lancom/shared/assets/js/utils/colors';
64
+ import { getProductLargeCover, getProductMediumCover } from '@lancom/shared/assets/js/utils/colors';
65
65
  import LazyHydrate from 'vue-lazy-hydration';
66
66
  import TheNavbar from '@/components/the_navbar/the-navbar';
67
67
  import TheChangesSavedIndicator from '@lancom/shared/components/the_changes_saved_indicator/the-changes-saved-indicator';
@@ -93,13 +93,17 @@
93
93
  middleware: ['page-info'],
94
94
  async fetch() {
95
95
  await this.loadProducts();
96
+ if (process.server) {
97
+ this.$root.context?.res?.setHeader('Cache-Control', `max-age=${86400}`);;
98
+ }
96
99
  },
97
100
  computed: {
98
101
  ...mapGetters('page', ['routeInfo']),
99
102
  ...mapGetters(['notificationBar', 'shop', 'country', 'currency']),
100
103
  ...mapGetters('products', ['category', 'categories', 'brands', 'types', 'tags', 'loadError', 'count', 'products', 'minPrice', 'maxPrice']),
101
104
  pageItemCanonical() {
102
- return this.breadcrumbs[this.breadcrumbs.length - 1]?.to;
105
+ const page = +this.$route.query.page;
106
+ return `${this.breadcrumbs[this.breadcrumbs.length - 1]?.to}${page > 1 ? `?page=${page}` : ''}`;
103
107
  },
104
108
  currentBrand() {
105
109
  return this.findByRouteParam(this.brands, 'brand');
@@ -252,7 +256,8 @@
252
256
  };
253
257
  }
254
258
 
255
- const image = getProductLargeCover(product, 'front');
259
+ const image = getProductLargeCover(product, 'front') || getProductMediumCover(product, 'front');
260
+ console.log('image: ', image, product.images);
256
261
  if (image) {
257
262
  schema.image = image;
258
263
  }
@@ -314,7 +319,8 @@
314
319
  ...this.$route.query,
315
320
  country: this.country?._id,
316
321
  currency: this.currency?._id,
317
- placeholder
322
+ placeholder,
323
+ limit: 80
318
324
  };
319
325
  try {
320
326
  await this.fetchProducts({ params, shop: this.shop._id });
@@ -322,7 +328,6 @@
322
328
  setTimeout(() => this.logGtm());
323
329
  } catch ({ response }) {
324
330
  if (process.server) {
325
- // console.log('status code: ', this.loadError, process.server, this._self.context, Object.keys(this));
326
331
  this.$root.context.res.statusCode = this.loadError?.statusCode || 500;
327
332
  }
328
333
  }
@@ -17,7 +17,7 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
17
17
  if (to && to.name === 'brand-tees-slug') { return 'slide-right-to-left'; };
18
18
  return 'fade';
19
19
  },
20
- async asyncData({ store, params, error, query, redirect }) {
20
+ async asyncData({ store, params, error, query, redirect, res }) {
21
21
  try {
22
22
  const { print, color, colour } = query;
23
23
  const { shop, country, stockCountry, currency } = store.getters;
@@ -63,6 +63,8 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
63
63
  error(loadError);
64
64
  }
65
65
 
66
+ res?.setHeader('Cache-Control', `max-age=${3600}`);
67
+
66
68
  return {
67
69
  isEditor,
68
70
  pageItem,
@@ -122,9 +124,10 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
122
124
  };
123
125
  if (!this.product) {
124
126
  await this.fetchProduct(data);
125
- await this.fetchProductDetails(data);
126
127
  }
127
128
 
129
+ await this.fetchProductDetails(data);
130
+
128
131
  try {
129
132
  if (this.preSetPrints?.length) {
130
133
  for (const preSetPrint of this.preSetPrints) {
@@ -235,7 +238,7 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
235
238
  const productSchema = {
236
239
  '@context': 'https://schema.org',
237
240
  '@type': 'Product',
238
- description: this.product.gsFeedDescription || this.product.description,
241
+ description: (this.product.gsFeedDescription || this.product.description || '').replace(/<[^>]*>/g, ''),
239
242
  name,
240
243
  offers: this.productDetails?.simpleProducts.map(sp => {
241
244
  const spMaxPrice = (sp.pricing || []).reduce((price, pricing) => Math.max(price, pricing.price), 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.350",
3
+ "version": "0.0.352",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
@@ -1,13 +1,17 @@
1
1
  module.exports = function (req, res, next) {
2
+ if (res.getHeader('Cache-Control')) {
3
+ return next();
4
+ }
5
+
2
6
  if (!/auth\=/.test(req.headers?.cookie || '')) {
3
7
  const routes = [
4
8
  /^\/quotes\//,
5
9
  /\.xml$/
6
10
  ];
7
11
  const value = routes.some(route => route.test(`${req.url}`)) ? 'no-cache' : `max-age=${31536000}`;
8
- res.setHeader('Cache-Control', value);
12
+ res?.setHeader('Cache-Control', value);
9
13
  } else {
10
- res.setHeader('Cache-Control', 'no-cache');
14
+ res?.setHeader('Cache-Control', 'no-cache');
11
15
  }
12
16
  next();
13
17
  };
package/store/product.js CHANGED
@@ -12,6 +12,7 @@ import { sortByName } from '../assets/js/utils/filters';
12
12
  export const state = () => ({
13
13
  multipack: null,
14
14
  calculatingPrice: false,
15
+ loadingProductDetails: false,
15
16
  images: [],
16
17
  priceIncludeGST: false,
17
18
  product: null,
@@ -49,6 +50,7 @@ export const state = () => ({
49
50
  });
50
51
 
51
52
  export const getters = {
53
+ loadingProductDetails: ({ loadingProductDetails }) => loadingProductDetails,
52
54
  multipack: ({ multipack }) => multipack,
53
55
  calculatingPrice: ({ calculatingPrice }) => calculatingPrice,
54
56
  product: ({ product }) => product,
@@ -177,8 +179,10 @@ export const actions = {
177
179
  },
178
180
  async fetchProductDetails({ commit, state }, { shop, slug, country, stockCountry, currency, defaultColor = 'white' }) {
179
181
  const params = { country, currency, stockCountry };
182
+ commit('setLoadingProductDetails', true);
180
183
  const response = await api.fetchProductDetails(shop, slug, params);
181
184
  commit('setProductDetails', response);
185
+ commit('setLoadingProductDetails', false);
182
186
 
183
187
  const catalogFrontColorId = getColorOfDefaultCatalogFrontImage(state.product);
184
188
  const editableColor = state.availableColors.find(c => c.alias === defaultColor) ||
@@ -273,6 +277,9 @@ export const actions = {
273
277
  };
274
278
 
275
279
  export const mutations = {
280
+ setLoadingProductDetails(state, loadingProductDetails) {
281
+ state.loadingProductDetails = loadingProductDetails;
282
+ },
276
283
  setMultipack(state, multipack) {
277
284
  state.multipack = multipack;
278
285
  },