@lancom/shared 0.0.430 → 0.0.431

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 (46) hide show
  1. package/assets/js/api/index.js +2 -2
  2. package/assets/js/constants/product.js +14 -0
  3. package/assets/scss/_common.scss +9 -1
  4. package/assets/scss/variables/_breakpoints.scss +2 -0
  5. package/components/common/breadcrumbs/breadcrumbs.scss +4 -1
  6. package/components/common/breadcrumbs/breadcrumbs.vue +4 -1
  7. package/components/common/client_settings/client-settings.vue +2 -3
  8. package/components/common/client_settings_tax/client-settings-tax.vue +1 -0
  9. package/components/common/price.vue +1 -2
  10. package/components/common/pricing_discounts_table/pricing-discounts-table.vue +28 -5
  11. package/components/common/product_side_with_print/product-side-with-print.vue +3 -3
  12. package/components/common/stars-mark.vue +21 -9
  13. package/components/common/tabs.vue +17 -3
  14. package/components/editor/editor_colors/editor-colors.scss +79 -0
  15. package/components/editor/editor_colors/editor-colors.vue +90 -0
  16. package/components/editor/editor_layers/editor-layers.vue +11 -6
  17. package/components/editor/editor_workspace/editor-workspace.vue +17 -15
  18. package/components/editor/editor_workspace/editor_workspace_side/editor-workspace-side.vue +2 -9
  19. package/components/product/other_products/other-products.scss +34 -0
  20. package/components/product/other_products/other-products.vue +76 -0
  21. package/components/product/other_products/other_product/other-product.scss +80 -0
  22. package/components/product/other_products/other_product/other-product.vue +59 -0
  23. package/components/product/product.vue +0 -14
  24. package/components/product/product_check_delivery/product-check-delivery.scss +31 -0
  25. package/components/product/product_check_delivery/product-check-delivery.vue +73 -0
  26. package/components/product/product_colors_selector/product-colors-selector.scss +8 -1
  27. package/components/product/product_colors_selector/product-colors-selector.vue +8 -0
  28. package/components/product/product_pricing_tiers/product-pricing-tiers.scss +53 -0
  29. package/components/product/product_pricing_tiers/product-pricing-tiers.vue +40 -0
  30. package/components/product/product_reviews/product-reviews.vue +6 -6
  31. package/components/product/product_stock_status/product-stock-status.scss +28 -0
  32. package/components/product/product_stock_status/product-stock-status.vue +35 -0
  33. package/components/product/related_products/related-products.vue +4 -4
  34. package/components/products/products_attributes/products_attribute/products-attribute.vue +19 -18
  35. package/components/products/products_brands/products-brands.vue +3 -1
  36. package/components/products/products_filters/products-filters.vue +9 -1
  37. package/components/products/products_tags/products-tags.vue +19 -18
  38. package/components/products/products_types/products-types.scss +1 -1
  39. package/layouts/products.vue +153 -432
  40. package/mixins/layouts/products.js +285 -0
  41. package/mixins/product-preview.js +13 -0
  42. package/package.json +1 -1
  43. package/store/index.js +2 -0
  44. package/store/product.js +50 -12
  45. package/components/the_breadcrumbs/the-breadcrumbs.scss +0 -36
  46. package/components/the_breadcrumbs/the-breadcrumbs.vue +0 -78
@@ -1,449 +1,170 @@
1
1
  <template>
2
- <div
3
- class="Products"
4
- :class="{
5
- 'has-notification': notificationBar.enabled
6
- }">
7
- <LazyHydrate when-idle>
8
- <the-navbar />
9
- </LazyHydrate>
10
- <the-changes-saved-indicator />
11
- <div class="content Products__wrapper">
12
- <div v-if="loadError">
13
- <error :error="loadError" />
14
- </div>
15
- <div
16
- v-else
17
- class="content-inner extra">
18
- <breadcrumbs :items="breadcrumbs" />
19
- <div class="Products__info">
20
- <h1 class="Products__name">
21
- {{ routeName }}
22
- </h1>
23
- <div class="Products__filters">
24
- <products-filters @open="openAside" />
25
- </div>
2
+ <div
3
+ class="Products"
4
+ :class="{
5
+ 'has-notification': notificationBar.enabled
6
+ }">
7
+ <LazyHydrate when-idle>
8
+ <the-navbar />
9
+ </LazyHydrate>
10
+ <the-changes-saved-indicator />
11
+ <div class="content Products__wrapper">
12
+ <div v-if="loadError">
13
+ <error :error="loadError" />
14
+ </div>
15
+ <div
16
+ v-else
17
+ class="content-inner extra">
18
+ <breadcrumbs :items="breadcrumbs" />
19
+ <div class="Products__info">
20
+ <h1 class="Products__name">
21
+ {{ routeName }}
22
+ </h1>
23
+ <div class="Products__filters">
24
+ <products-filters @open="openAside" />
26
25
  </div>
27
- <div class="Products__content">
28
- <div class="Products__aside">
29
- <breakpoint
30
- name="md"
31
- mode="up">
32
- <products-aside />
33
- </breakpoint>
34
- </div>
35
- <div class="Products__list">
36
- <children-categories v-if="hasChildrenCategories" />
37
- <products-catalog class="Products__catalog" />
38
- </div>
26
+ </div>
27
+ <div class="Products__content">
28
+ <div class="Products__aside">
29
+ <breakpoint
30
+ name="md"
31
+ mode="up">
32
+ <products-aside />
33
+ </breakpoint>
39
34
  </div>
40
- <div
41
- v-if="routeInfo && routeInfo.text"
42
- class="Products__text">
43
- <LazyHydrate never>
44
- <static-page
45
- :visible-title="false"
46
- :item="routeInfo" />
47
- </LazyHydrate>
35
+ <div class="Products__list">
36
+ <children-categories v-if="hasChildrenCategories" />
37
+ <products-catalog class="Products__catalog" />
48
38
  </div>
49
39
  </div>
40
+ <div
41
+ v-if="routeInfo && routeInfo.text"
42
+ class="Products__text">
43
+ <LazyHydrate never>
44
+ <static-page
45
+ :visible-title="false"
46
+ :item="routeInfo" />
47
+ </LazyHydrate>
48
+ </div>
50
49
  </div>
51
- <LazyHydrate when-idle>
52
- <the-footer />
53
- </LazyHydrate>
54
- <modals-container />
55
- <the-aside />
56
50
  </div>
57
- </template>
58
-
59
- <script>
60
- import debounce from 'lodash.debounce';
61
- import LazyHydrate from 'vue-lazy-hydration';
62
- import { mapActions, mapGetters, mapMutations } from 'vuex';
63
- import gtm from '@lancom/shared/assets/js/utils/gtm';
64
- import metaInfo from '@lancom/shared/mixins/meta-info';
65
- import { generateProductsLink, generateProductLink } from '@lancom/shared/assets/js/utils/product';
66
- import { getProductLargeCover, getProductMediumCover } from '@lancom/shared/assets/js/utils/colors';
67
- import TheChangesSavedIndicator from '@lancom/shared/components/the_changes_saved_indicator/the-changes-saved-indicator';
68
- import Breadcrumbs from '@lancom/shared/components/common/breadcrumbs/breadcrumbs';
69
- import StaticPage from '@lancom/shared/components/static_page/static-page';
70
- import TheAside from '@lancom/shared/components/the_aside/the-aside';
71
- import ProductsFilters from '@lancom/shared/components/products/products_filters/products-filters';
72
- import ChildrenCategories from '@lancom/shared/components/products/children_categories/children-categories';
73
- import ProductsAside from '@/components/products/products_aside/products-aside';
74
- import TheNavbar from '@/components/the_navbar/the-navbar';
75
- import TheFooter from '@/components/the_footer/the-footer';
76
- import ProductsCatalog from '@/components/products/products_catalog/products-catalog';
77
- import Error from './error';
78
-
79
- export default {
80
- name: 'ProductsLayout',
81
- components: {
82
- LazyHydrate,
83
- TheNavbar,
84
- Breadcrumbs,
85
- TheFooter,
86
- TheAside,
87
- ProductsAside,
88
- ProductsFilters,
89
- ProductsCatalog,
90
- ChildrenCategories,
91
- Error,
92
- StaticPage,
93
- TheChangesSavedIndicator
94
- },
95
- mixins: [metaInfo],
96
- middleware: ['page-info'],
97
- async fetch() {
98
- await this.loadProducts();
99
-
100
- if (this.page === 1) {
101
- await this.fetchChildrenCategories(this.currentCategory);
102
- }
103
-
104
- if (process.server) {
105
- this.$root.context?.res?.setHeader('Cache-Control', `max-age=${86400}`);;
106
- }
107
- },
108
- computed: {
109
- ...mapGetters('page', ['routeInfo']),
110
- ...mapGetters(['notificationBar', 'shop', 'country', 'currency']),
111
- ...mapGetters('products', ['category', 'childrenCategories', 'categories', 'brands', 'types', 'tags', 'loadError', 'count', 'products', 'minPrice', 'maxPrice', 'page']),
112
- hasChildrenCategories() {
113
- return this.childrenCategories.length > 0;
114
- },
115
- pageItemCanonical() {
116
- const page = +this.$route.query.page;
117
- return `${this.breadcrumbs[this.breadcrumbs.length - 1]?.to}${page > 1 ? `?page=${page}` : ''}`;
118
- },
119
- pageItemType() {
120
- return 'product';
121
- },
122
- pageItemImage() {
123
- const images = this.products?.reduce((images, product) => {
124
- const image = getProductLargeCover(product, 'front') || getProductMediumCover(product, 'front');
125
- return image ? [...images, image] : images;
126
- }, []) || [];
127
- return images.slice(0, 1);
128
- },
129
- currentBrand() {
130
- return this.findByRouteParam(this.brands, 'brand');
131
- },
132
- currentTag() {
133
- return this.findByRouteParam(this.tags, 'tag');
134
- },
135
- currentProductType() {
136
- return this.findByRouteParam(this.types, 'type');
137
- },
138
- currentCategory() {
139
- return this.category;
140
- },
141
- breadcrumbs() {
142
- const breadcrumbs = [{
143
- to: '/products',
144
- text: 'Products'
145
- }];
146
- if (this.currentCategory) {
147
- const categoriesBreadcrumbs = [];
148
- let category = this.currentCategory;
149
- console.log('category: ', category);
150
- while (category) {
151
- categoriesBreadcrumbs.unshift({
152
- to: generateProductsLink(this.$route, {
153
- type: null,
154
- brand: null,
155
- tag: null,
156
- category
157
- }, true),
158
- text: category.name
159
- });
160
- category = category.parent;
161
- }
162
- breadcrumbs.push(...categoriesBreadcrumbs);
163
- }
164
- if (this.currentProductType) {
165
- breadcrumbs.push({
166
- to: generateProductsLink(this.$route, {
167
- type: this.currentProductType.alias,
168
- brand: null,
169
- tag: null,
170
- category: null
171
- }),
172
- text: this.currentProductType.name
173
- });
174
- }
175
-
176
- if (this.currentTag) {
177
- breadcrumbs.push({
178
- to: generateProductsLink(this.$route, {
179
- type: this.currentProductType?.alias,
180
- tag: this.currentTag?.alias,
181
- brand: null,
182
- category: null
183
- }),
184
- text: this.currentTag.name
185
- });
186
- }
187
-
188
- if (this.currentBrand) {
189
- breadcrumbs.push({
190
- to: generateProductsLink(this.$route, {
191
- type: this.currentProductType?.alias,
192
- tag: this.currentTag?.alias,
193
- brand: this.currentBrand?.alias,
194
- category: this.currentCategory?.alias
195
- }),
196
- text: this.currentBrand.name
197
- });
198
- }
199
-
200
- return breadcrumbs;
201
- },
202
- routeName() {
203
- if (this.routeInfo && this.routeInfo.textTitle) {
204
- return this.routeInfo.textTitle;
205
- }
206
-
207
- const items = ['Products'];
208
- if (this.currentCategory) {
209
- items.push(this.currentCategory.name);
210
- }
211
- if (this.currentProductType) {
212
- items.push(this.currentProductType.name);
213
- }
214
- if (this.currentTag) {
215
- items.push(this.currentTag.name);
216
- }
217
- if (this.currentBrand) {
218
- items.push(this.currentBrand.name);
219
- }
220
- return items.join(' / ');
221
- }
222
- },
223
- watch: {
224
- '$route.params': {
225
- handler() {
226
- this.loadProductsWithDebounce();
227
- },
228
- deep: true
229
- },
230
- '$route.query': {
231
- handler() {
232
- this.loadProductsWithDebounce();
233
- },
234
- deep: true
235
- }
236
- },
237
- mounted() {
238
- this.loadState(this.$route.query);
239
- this.logGtm();
240
- // setTimeout(() => this.loadProducts(), 3000);
241
- },
242
- jsonld() {
243
- const productsSchema = {
244
- '@context': 'http://schema.org',
245
- '@type': 'OfferCatalog',
246
- name: this.pageTitle,
247
- numberOfItems: this.count,
248
- itemListOrder: 'ItemListUnordered',
249
- itemListElement: this.products?.map(product => {
250
- const minPrintsPrice = product.minPrintsPrice || 0;
251
- const minProductPrice = product.isClearance ? product.minPriceWithoutClearance : product.minPrice;
252
- const minPrice = minProductPrice + minPrintsPrice;
253
-
254
- const maxPrintsPrice = product.maxPrintsPrice || 0;
255
- const maxProductPrice = product.isClearance ? product.maxPriceWithoutClearance : product.maxPrice;
256
- const maxPrice = maxProductPrice + maxPrintsPrice;
257
-
258
- const schema = {
259
- '@type': 'Product',
260
- name: product.name,
261
- url: `https://${process.env.HOST_NAME}${generateProductLink(product)}`,
262
- sku: product.SKU,
263
- offers: {
264
- '@type': 'AggregateOffer',
265
- offerCount: 1,
266
- name: product.name,
267
- highPrice: maxPrice,
268
- lowPrice: minPrice,
269
- priceCurrency: this.currency?.isoCode || 'AUD',
270
- availability: 'InStock'
271
- }
272
- };
273
- if (product.brand) {
274
- schema.brand = {
275
- '@type': 'Organization',
276
- name: product.brand.name
277
- };
278
- }
279
-
280
- const image = getProductLargeCover(product, 'front') || getProductMediumCover(product, 'front');
281
- if (image) {
282
- schema.image = image;
283
- }
284
-
285
- return schema;
286
- }) || []
287
- };
288
-
289
- const breadcrumbSchema = {
290
- '@context': 'https://schema.org',
291
- '@type': 'BreadcrumbList',
292
- itemListElement: [
293
- {
294
- '@type': 'ListItem',
295
- position: 1,
296
- name: 'Home',
297
- item: `https://${process.env.HOST_NAME}/`
298
- },
299
- ...this.breadcrumbs
300
- .map((b, index) => ({
301
- '@type': 'ListItem',
302
- position: index + 2,
303
- name: b.text,
304
- item: b.to ? `https://${process.env.HOST_NAME}${b.to}` : undefined
305
- }))
306
- ]
307
- };
308
-
309
- return [productsSchema, breadcrumbSchema, this.$store.state.shop?.shopSchema].filter(s => !!s);
310
- },
311
- methods: {
312
- ...mapActions([
313
- 'loadState'
314
- ]),
315
- ...mapActions('products', [
316
- 'fetchProducts',
317
- 'fetchChildrenCategories'
318
- ]),
319
- ...mapMutations('products', [
320
- 'setPlaceholder'
321
- ]),
322
- logGtm() {
323
- if (process.client) {
324
- if (this.$route.query.text) {
325
- gtm.viewSearchResult(this.$route.query.text);
326
- }
327
- gtm.viewItemList(this.products, this.currency);
328
- }
329
- },
330
- loadProductsWithDebounce: debounce(function () {
331
- if (/^\/products/.test(this.$route.path)) {
332
- this.loadProducts();
333
- }
334
-
335
- this.$aside.hide();
336
- }, 100),
337
- async loadProducts(placeholder) {
338
- const params = {
339
- ...this.$route.params,
340
- ...this.$route.query,
341
- country: this.country?._id,
342
- currency: this.currency?._id,
343
- placeholder,
344
- limit: 80
345
- };
346
- try {
347
- await this.fetchProducts({ params, shop: this.shop._id });
348
- this.setPlaceholder(placeholder);
349
- setTimeout(() => this.logGtm());
350
- } catch ({ response }) {
351
- if (process.server) {
352
- this.$root.context.res.statusCode = this.loadError?.statusCode || 500;
353
- }
354
- }
355
- },
356
- findByRouteParam(list, param) {
357
- return list.find(i => i.alias === this.$route.params[param]);
358
- },
359
- fillWithItemData(text = '') {
360
- text = this.currentBrand ? text.replace(/{{brandName}}/g, this.currentBrand.name) : text;
361
- text = this.currentProductType ? text.replace(/{{typeName}}/g, this.currentProductType.name) : text;
362
- text = this.currentTag ? text.replace(/{{tagName}}/g, this.currentTag.name) : text;
363
- text = this.currentCategory ? text.replace(/{{categoryName}}/g, this.currentCategory.name) : text;
364
- return text;
365
- },
366
- async openAside() {
367
- const ProductsAside = await import('@/components/products/products_aside/products-aside');
368
- this.$aside.show(ProductsAside.default);
369
- }
370
- }
371
- };
372
- </script>
373
- <style lang="scss" scoped>
374
- @import "@/assets/scss/variables";
375
-
376
- .Products {
377
- margin-top: 40px;
378
- &__text {
379
- margin-top: 30px;
51
+ <LazyHydrate when-idle>
52
+ <the-footer />
53
+ </LazyHydrate>
54
+ <modals-container />
55
+ <the-aside />
56
+ </div>
57
+ </template>
58
+
59
+ <script>
60
+ import LazyHydrate from 'vue-lazy-hydration';
61
+ import products from '@lancom/shared/mixins/layouts/products';
62
+ import TheChangesSavedIndicator from '@lancom/shared/components/the_changes_saved_indicator/the-changes-saved-indicator';
63
+ import Breadcrumbs from '@lancom/shared/components/common/breadcrumbs/breadcrumbs';
64
+ import StaticPage from '@lancom/shared/components/static_page/static-page';
65
+ import TheAside from '@lancom/shared/components/the_aside/the-aside';
66
+ import ProductsFilters from '@lancom/shared/components/products/products_filters/products-filters';
67
+ import ChildrenCategories from '@lancom/shared/components/products/children_categories/children-categories';
68
+ import Error from '@lancom/shared/layouts/error';
69
+ import ProductsAside from '@/components/products/products_aside/products-aside';
70
+ import TheNavbar from '@/components/the_navbar/the-navbar';
71
+ import TheFooter from '@/components/the_footer/the-footer';
72
+ import ProductsCatalog from '@/components/products/products_catalog/products-catalog';
73
+
74
+ export default {
75
+ name: 'ProductsLayout',
76
+ components: {
77
+ LazyHydrate,
78
+ TheNavbar,
79
+ Breadcrumbs,
80
+ TheFooter,
81
+ TheAside,
82
+ ProductsAside,
83
+ ProductsFilters,
84
+ ProductsCatalog,
85
+ ChildrenCategories,
86
+ Error,
87
+ StaticPage,
88
+ TheChangesSavedIndicator
89
+ },
90
+ mixins: [products]
91
+ };
92
+ </script>
93
+
94
+ <style lang="scss" scoped>
95
+ @import "@/assets/scss/variables";
96
+
97
+ .Products {
98
+ margin-top: 40px;
99
+ &__text {
100
+ margin-top: 30px;
101
+ }
102
+ &__content {
103
+ background: $white;
104
+ padding-left: 24px;
105
+ display: flex;
106
+ justify-content: space-between;
107
+ @media (max-width: $bp-small-max) {
108
+ padding-left: 0px;
380
109
  }
381
- &__content {
382
- background: $white;
383
- padding-left: 24px;
110
+ }
111
+ &__filters {
112
+ position: relative;
113
+ z-index: 2;
114
+ @media (max-width: $bp-medium-max) {
384
115
  display: flex;
385
- justify-content: space-between;
386
- @media (max-width: $bp-small-max) {
387
- padding-left: 0px;
388
- }
389
- }
390
- &__filters {
391
- position: relative;
392
- z-index: 2;
393
- @media (max-width: $bp-medium-max) {
394
- display: flex;
395
- width: 100%;
396
- justify-content: flex-end;
397
- margin-top: 20px;
398
- }
399
- }
400
- &__aside {
401
- width: 215px;
402
- flex-grow: 1;
403
- flex-shrink: 0;
404
- @media (max-width: $bp-small-max) {
405
- display: none;
406
- }
407
- }
408
- &__list {
409
116
  width: 100%;
117
+ justify-content: flex-end;
118
+ margin-top: 20px;
410
119
  }
411
- &__info {
412
- display: flex;
413
- justify-content: space-between;
414
- align-items: center;
415
- margin: 25px 0;
416
- @media (max-width: $bp-medium-max) {
417
- flex-direction: column;
418
- justify-content: start;
419
- align-items: flex-start;
420
- margin: 5px 0;
421
- }
120
+ }
121
+ &__aside {
122
+ width: 215px;
123
+ flex-grow: 1;
124
+ flex-shrink: 0;
125
+ @media (max-width: $bp-small-max) {
126
+ display: none;
422
127
  }
423
- &__name {
424
- font-weight: 800;
425
- font-size: 36px;
426
- line-height: 49px;
427
- color: $black;
428
- text-transform: uppercase;
429
- @media (max-width: $bp-medium-max) {
430
- font-size: 20px;
431
- line-height: 29px;
432
- }
128
+ }
129
+ &__list {
130
+ width: 100%;
131
+ }
132
+ &__info {
133
+ display: flex;
134
+ justify-content: space-between;
135
+ align-items: center;
136
+ margin: 25px 0;
137
+ @media (max-width: $bp-medium-max) {
138
+ flex-direction: column;
139
+ justify-content: start;
140
+ align-items: flex-start;
141
+ margin: 5px 0;
433
142
  }
434
143
  }
435
- ::v-deep .ProductsFilters {
436
- &__wrapper {
437
- margin-bottom: 20px;
438
-
439
- input,
440
- .multiselect__tags {
441
- border-radius: 0px !important;
442
- }
144
+ &__name {
145
+ font-weight: 800;
146
+ font-size: 36px;
147
+ line-height: 49px;
148
+ color: $black;
149
+ text-transform: uppercase;
150
+ @media (max-width: $bp-medium-max) {
151
+ font-size: 20px;
152
+ line-height: 29px;
443
153
  }
444
- &__search {
445
- margin-left: 16px;
446
- width: 500px;
154
+ }
155
+ }
156
+ ::v-deep .ProductsFilters {
157
+ &__wrapper {
158
+ margin-bottom: 20px;
159
+
160
+ input,
161
+ .multiselect__tags {
162
+ border-radius: 0px !important;
447
163
  }
448
164
  }
449
- </style>
165
+ &__search {
166
+ margin-left: 16px;
167
+ width: 500px;
168
+ }
169
+ }
170
+ </style>