@lancom/shared 0.0.460 → 0.0.462

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 (25) hide show
  1. package/assets/js/api/admin.js +3 -0
  2. package/assets/js/api/index.js +10 -0
  3. package/assets/scss/variables/_theme.scss +12 -0
  4. package/components/common/file_uploader.vue +2 -1
  5. package/components/common/toggle-content.vue +9 -2
  6. package/components/products/children_categories/children-categories.vue +1 -1
  7. package/components/products/products_aside/products-aside.scss +1 -1
  8. package/components/products/products_aside/products-aside.vue +50 -12
  9. package/components/products/products_attributes/products-attributes.vue +6 -1
  10. package/components/products/products_attributes/products_attribute/products-attribute.vue +6 -2
  11. package/components/products/products_brands/products-brands.vue +6 -2
  12. package/components/products/products_color_groups/products-color-groups.scss +29 -0
  13. package/components/products/products_color_groups/products-color-groups.vue +48 -4
  14. package/components/products/products_colors/products-colors.scss +30 -1
  15. package/components/products/products_colors/products-colors.vue +48 -4
  16. package/components/products/products_minimum_qty/products-minimum-qty.vue +5 -1
  17. package/components/products/products_production_time/products-production-time.vue +5 -1
  18. package/components/products/products_selected_filters/products-selected-filters.scss +59 -0
  19. package/components/products/products_selected_filters/products-selected-filters.vue +109 -0
  20. package/components/products/products_tags/products-tags.vue +5 -1
  21. package/components/products/products_types/products-types.vue +5 -1
  22. package/components/static_page/static-page.scss +6 -0
  23. package/mixins/product-preview.js +3 -0
  24. package/mixins/products/selected-filters.js +49 -0
  25. package/package.json +1 -1
@@ -329,6 +329,9 @@ export default {
329
329
  importProductImagesJson(productId, images) {
330
330
  return _post(`admin/products/${productId}/import-images-json`, { images });
331
331
  },
332
+ importProductDocumentsJson(productId, documents) {
333
+ return _post(`admin/products/${productId}/import-documents-json`, { documents });
334
+ },
332
335
  fetchProductsKits(params) {
333
336
  return _get('admin/products-kit', params);
334
337
  },
@@ -178,6 +178,16 @@ const api = {
178
178
  }
179
179
  return _post(requestUrl, formData, config);
180
180
  },
181
+ uploadFile(file, progressCallback = Function.prototype, requestUrl = 'file/upload') {
182
+ const config = {
183
+ onUploadProgress: ({ loaded, total }) => {
184
+ progressCallback(Math.round((loaded * 100) / total));
185
+ }
186
+ };
187
+ const formData = new FormData();
188
+ formData.set('file', file);
189
+ return _post(requestUrl, formData, config);
190
+ },
181
191
  sendFailedConversionFile(data, shop) {
182
192
  return _post(`shop/${shop}/image/send-failed-conversion`, data);
183
193
  },
@@ -1,5 +1,17 @@
1
1
  // theme variables
2
2
 
3
+ :root {
4
+ --accu-primary: #3B4BA4; /* Blue accent color */
5
+ --accu-bg: #F5F0EB; /* Warm cream background */
6
+ --accu-card: #FFFFFF; /* Card background */
7
+ --accu-text-dark: #1E1E1E; /* Primary text */
8
+ --accu-text-muted: #666666; /* Secondary text */
9
+ --accu-star: #FF9800; /* Star rating color */
10
+ --accu-green: #22C55E; /* Success/shipping color */
11
+ --accu-border: #E5E5E5; /* Border color */
12
+ --accu-hover: #f8f8f8; /* Hover state */
13
+ }
14
+
3
15
  // Layouts
4
16
  $navbar_height: 88px !default;
5
17
  $notification_bar_height: 40px !default;
@@ -54,6 +54,7 @@ export default {
54
54
  hasUploadLink: { type: Boolean, default: false },
55
55
  hasConversionErrorModal: { type: Boolean, default: true },
56
56
  showErrorMessage: { type: Boolean, default: true },
57
+ checkStaticUrl: { type: Boolean, default: true },
57
58
  url: { type: String },
58
59
  accept: { type: String, default: 'all' }
59
60
  },
@@ -116,7 +117,7 @@ export default {
116
117
  try {
117
118
  const image = await api.uploadImage(this.file, this.onProgress, this.url, this.convert);
118
119
  this.onProgress(0);
119
- const url = image && staticLink(image.thumb || image.origin);
120
+ const url = this.checkStaticUrl ? (image && staticLink(image.thumb || image.origin)) : image?.url;
120
121
  this.$emit('onuploaded', { ...image, url });
121
122
  } catch (e) {
122
123
  console.dir(e);
@@ -3,10 +3,11 @@
3
3
  <div
4
4
  class="ToggleContent__header"
5
5
  @click="toggle()">
6
- <div class="ToggleContent__label">
6
+ <div class="ToggleContent__label accu-filter-header">
7
7
  {{ label }}
8
8
  </div>
9
9
  <div
10
+ v-if="toggleable"
10
11
  class="ToggleContent__arrow"
11
12
  :class="{ opened }">
12
13
  <i class="icon-arrow-left"></i>
@@ -27,6 +28,10 @@ export default {
27
28
  label: {
28
29
  type: String,
29
30
  required: true
31
+ },
32
+ toggleable: {
33
+ type: Boolean,
34
+ default: true
30
35
  }
31
36
  },
32
37
  data() {
@@ -36,7 +41,9 @@ export default {
36
41
  },
37
42
  methods: {
38
43
  toggle() {
39
- this.opened = !this.opened;
44
+ if (this.toggleable) {
45
+ this.opened = !this.opened;
46
+ }
40
47
  }
41
48
  }
42
49
  };
@@ -8,7 +8,7 @@
8
8
  'CategoryCard--selected': category && category._id === cat._id
9
9
  }">
10
10
  <a
11
- :href="generateProductsLink($route, { category: cat }, true)"
11
+ :href="generateProductsLink($route, { category: (category && category._id === cat._id) ? (cat.parent || cat) : cat }, true)"
12
12
  class="CategoryCard__link">
13
13
  <span
14
14
  v-if="visibleImages"
@@ -43,4 +43,4 @@
43
43
  100% {
44
44
  background: #d9d9d9;
45
45
  }
46
- }
46
+ }
@@ -10,45 +10,64 @@
10
10
  <span class="ProductsAside__info ProductsAside__info--small"></span>
11
11
  <span class="ProductsAside__info ProductsAside__info--small2"></span>
12
12
  </div>
13
- <div v-else>
13
+ <div
14
+ v-else
15
+ class="ProductsAside__list">
16
+ <div
17
+ v-if="hasSelectedFilters"
18
+ class="ProductsAside__item">
19
+ <products-selected-filters />
20
+ </div>
14
21
  <div
15
22
  v-if="hasProductTypes"
16
23
  class="ProductsAside__item">
17
24
  <products-types
18
25
  :has-selected-icon="hasSelectedIcon"
19
- :selected-icon-circle="selectedIconCircle" />
26
+ :selected-icon-circle="selectedIconCircle"
27
+ :toggleable="toggleable" />
20
28
  </div>
21
29
  <div class="ProductsAside__item">
22
30
  <products-brands
23
31
  :has-selected-icon="hasSelectedIcon"
24
- :selected-icon-circle="selectedIconCircle" />
32
+ :selected-icon-circle="selectedIconCircle"
33
+ :toggleable="toggleable" />
25
34
  </div>
26
35
  <div
27
36
  v-if="hasProductionTime"
28
37
  class="ProductsAside__item">
29
38
  <products-production-time
30
39
  :has-selected-icon="hasSelectedIcon"
31
- :selected-icon-circle="selectedIconCircle" />
40
+ :selected-icon-circle="selectedIconCircle"
41
+ :toggleable="toggleable" />
32
42
  </div>
33
43
  <div
34
44
  v-if="hasMinimumQty"
35
45
  class="ProductsAside__item">
36
46
  <products-minimum-qty
37
47
  :has-selected-icon="hasSelectedIcon"
38
- :selected-icon-circle="selectedIconCircle" />
39
- </div>
40
- <div class="ProductsAside__item">
41
- <products-color-groups />
48
+ :selected-icon-circle="selectedIconCircle"
49
+ :toggleable="toggleable" />
42
50
  </div>
43
51
  <div class="ProductsAside__item">
44
- <products-colors />
52
+ <products-color-groups
53
+ :toggleable="toggleable"
54
+ :display-mode="colorDisplayMode" />
55
+ <products-colors
56
+ :toggleable="toggleable"
57
+ :display-mode="colorDisplayMode" />
45
58
  </div>
46
59
  <div class="ProductsAside__item">
47
60
  <products-attributes
48
- :has-selected-icon="hasSelectedIcon" />
61
+ :has-selected-icon="hasSelectedIcon"
62
+ :toggleable="toggleable" />
63
+ </div>
64
+ <div
65
+ v-if="hasTags"
66
+ class="ProductsAside__item">
49
67
  <products-tags
50
68
  :has-selected-icon="haAttributesSelectedIcon"
51
- :has-remove-icon="hasRemoveIcon" />
69
+ :has-remove-icon="hasRemoveIcon"
70
+ :toggleable="toggleable" />
52
71
  </div>
53
72
  </div>
54
73
  </div>
@@ -64,6 +83,7 @@ import ProductsColors from '@lancom/shared/components/products/products_colors/p
64
83
  import ProductsColorGroups from '@lancom/shared/components/products/products_color_groups/products-color-groups';
65
84
  import ProductsProductionTime from '@lancom/shared/components/products/products_production_time/products-production-time';
66
85
  import ProductsMinimumQty from '@lancom/shared/components/products/products_minimum_qty/products-minimum-qty';
86
+ import ProductsSelectedFilters from '@lancom/shared/components/products/products_selected_filters/products-selected-filters';
67
87
  import { generateProductsLink } from '@lancom/shared/assets/js/utils/product';
68
88
 
69
89
  export default {
@@ -76,7 +96,8 @@ export default {
76
96
  ProductsColors,
77
97
  ProductsColorGroups,
78
98
  ProductsProductionTime,
79
- ProductsMinimumQty
99
+ ProductsMinimumQty,
100
+ ProductsSelectedFilters
80
101
  },
81
102
  props: {
82
103
  hasSelectedIcon: {
@@ -110,6 +131,23 @@ export default {
110
131
  hasMinimumQty: {
111
132
  type: Boolean,
112
133
  default: true
134
+ },
135
+ hasTags: {
136
+ type: Boolean,
137
+ default: true
138
+ },
139
+ toggleable: {
140
+ type: Boolean,
141
+ default: true
142
+ },
143
+ colorDisplayMode: {
144
+ type: String,
145
+ default: 'grid', // 'grid' or 'list'
146
+ validator: value => ['grid', 'list'].includes(value)
147
+ },
148
+ hasSelectedFilters: {
149
+ type: Boolean,
150
+ default: false
113
151
  }
114
152
  },
115
153
  data() {
@@ -8,7 +8,8 @@
8
8
  class="ProductsAttributes__item">
9
9
  <products-attribute
10
10
  :attribute="attribute"
11
- :has-selected-icon="hasSelectedIcon" />
11
+ :has-selected-icon="hasSelectedIcon"
12
+ :toggleable="toggleable" />
12
13
  </div>
13
14
  </div>
14
15
  </template>
@@ -27,6 +28,10 @@ export default {
27
28
  hasSelectedIcon: {
28
29
  type: Boolean,
29
30
  default: false
31
+ },
32
+ toggleable: {
33
+ type: Boolean,
34
+ default: true
30
35
  }
31
36
  },
32
37
  computed: {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="ProductsAttribute__wrapper">
3
- <toggle-content :label="attribute.name">
3
+ <toggle-content :label="attribute.name" :toggleable="toggleable">
4
4
  <div class="ProductsAttribute__options">
5
5
  <div
6
6
  v-for="option in options"
@@ -13,7 +13,7 @@
13
13
  <checked-icon
14
14
  v-if="hasSelectedIcon"
15
15
  :checked="selectedOptions.includes(option.alias)" />
16
- <span>{{ option.name }}</span>
16
+ <span class="accu-filter-item">{{ option.name }}</span>
17
17
  </products-link>
18
18
  </div>
19
19
  </div>
@@ -37,6 +37,10 @@ export default {
37
37
  type: Boolean,
38
38
  default: false
39
39
  },
40
+ toggleable: {
41
+ type: Boolean,
42
+ default: true
43
+ },
40
44
  attribute: {
41
45
  type: Object
42
46
  }
@@ -2,7 +2,7 @@
2
2
  <div
3
3
  v-if="list.length"
4
4
  class="ProductsBrands__wrapper">
5
- <toggle-content label="Brands">
5
+ <toggle-content label="Brands" :toggleable="toggleable">
6
6
  <div
7
7
  v-for="brand in list"
8
8
  :key="brand.alias"
@@ -17,7 +17,7 @@
17
17
  v-if="hasSelectedIcon"
18
18
  :checked="brand.alias === currentBrand"
19
19
  :circle="selectedIconCircle" />
20
- <span>
20
+ <span class="accu-filter-item">
21
21
  {{ brand.name }}
22
22
  </span>
23
23
  </products-link>
@@ -47,6 +47,10 @@ export default {
47
47
  selectedIconCircle: {
48
48
  type: Boolean,
49
49
  default: false
50
+ },
51
+ toggleable: {
52
+ type: Boolean,
53
+ default: true
50
54
  }
51
55
  },
52
56
  computed: {
@@ -66,4 +66,33 @@
66
66
  cursor: pointer;
67
67
  text-align: right;
68
68
  }
69
+ &__list {
70
+ margin-top: 20px;
71
+ &-item {
72
+ display: flex;
73
+ align-items: center;
74
+ padding: 8px 0;
75
+ cursor: pointer;
76
+ transition: background-color 0.2s;
77
+ &--selected {
78
+ .ProductsColorGroups__list-label {
79
+ font-weight: 600;
80
+ color: $grey_1;
81
+ }
82
+ }
83
+ }
84
+ &-checkbox {
85
+ flex-shrink: 0;
86
+ margin-right: 12px;
87
+ }
88
+ &-thumb {
89
+ flex-shrink: 0;
90
+ margin-right: 12px;
91
+ }
92
+ &-label {
93
+ flex-grow: 1;
94
+ font-size: 14px;
95
+ color: $black;
96
+ }
97
+ }
69
98
  }
@@ -1,7 +1,9 @@
1
1
  <template>
2
2
  <div class="ProductsColorGroups__wrapper">
3
- <toggle-content label="Colour Groups">
4
- <div class="ProductsColorGroups__selected">
3
+ <toggle-content label="Colour Groups" :toggleable="toggleable">
4
+ <div
5
+ v-if="displayMode === 'grid'"
6
+ class="ProductsColorGroups__selected">
5
7
  <div
6
8
  v-for="colorGroup in selectedColorGroups"
7
9
  :key="colorGroup._id"
@@ -19,7 +21,9 @@
19
21
  </products-link>
20
22
  </div>
21
23
  </div>
22
- <div class="ProductsColorGroups__items">
24
+ <div
25
+ v-if="displayMode === 'grid'"
26
+ class="ProductsColorGroups__items">
23
27
  <products-link
24
28
  v-for="colorGroup in visibleColorGroups"
25
29
  :key="colorGroup._id"
@@ -37,6 +41,33 @@
37
41
  </div>
38
42
  </products-link>
39
43
  </div>
44
+ <div
45
+ v-else
46
+ class="ProductsColorGroups__list">
47
+ <products-link
48
+ v-for="colorGroup in visibleColorGroups"
49
+ :key="colorGroup._id"
50
+ :color-group="colorGroup.alias">
51
+ <div
52
+ class="ProductsColorGroups__list-item"
53
+ :class="{
54
+ 'ProductsColorGroups__list-item--selected': isSelectedColorGroup(colorGroup)
55
+ }">
56
+ <div class="ProductsColorGroups__list-checkbox">
57
+ <checked-icon :checked="isSelectedColorGroup(colorGroup)" />
58
+ </div>
59
+ <div class="ProductsColorGroups__list-thumb ProductsColorGroups__color-group">
60
+ <product-color-image
61
+ v-if="canLoadImages"
62
+ :color="colorGroup"
63
+ class="ProductsColorGroups__color-group-image" />
64
+ </div>
65
+ <div class="ProductsColorGroups__list-label accu-filter-item">
66
+ {{ colorGroup.name }}
67
+ </div>
68
+ </div>
69
+ </products-link>
70
+ </div>
40
71
  <div
41
72
  v-if="shortListColorGroupsLimit < colorGroupsCount"
42
73
  @click="toggleDisplayAllColorGroups()"
@@ -52,13 +83,26 @@ import { mapGetters } from 'vuex';
52
83
  import ToggleContent from '@lancom/shared/components/common/toggle-content';
53
84
  import ProductsLink from '@lancom/shared/components/products/products_link/products-link';
54
85
  import ProductColorImage from '@lancom/shared/components/product/product_color_image/product-color-image';
86
+ import CheckedIcon from '@lancom/shared/components/common/checked-icon';
55
87
 
56
88
  export default {
57
89
  name: 'ProductsColorGroups',
58
90
  components: {
59
91
  ProductColorImage,
60
92
  ProductsLink,
61
- ToggleContent
93
+ ToggleContent,
94
+ CheckedIcon
95
+ },
96
+ props: {
97
+ toggleable: {
98
+ type: Boolean,
99
+ default: true
100
+ },
101
+ displayMode: {
102
+ type: String,
103
+ default: 'grid', // 'grid' or 'list'
104
+ validator: value => ['grid', 'list'].includes(value)
105
+ }
62
106
  },
63
107
  data() {
64
108
  return {
@@ -66,4 +66,33 @@
66
66
  cursor: pointer;
67
67
  text-align: right;
68
68
  }
69
- }
69
+ &__list {
70
+ margin-top: 20px;
71
+ &-item {
72
+ display: flex;
73
+ align-items: center;
74
+ padding: 8px 0;
75
+ cursor: pointer;
76
+ transition: background-color 0.2s;
77
+ &--selected {
78
+ .ProductsColorGroups__list-label {
79
+ font-weight: 600;
80
+ color: $grey_1;
81
+ }
82
+ }
83
+ }
84
+ &-checkbox {
85
+ flex-shrink: 0;
86
+ margin-right: 12px;
87
+ }
88
+ &-thumb {
89
+ flex-shrink: 0;
90
+ margin-right: 12px;
91
+ }
92
+ &-label {
93
+ flex-grow: 1;
94
+ font-size: 14px;
95
+ color: $black;
96
+ }
97
+ }
98
+ }
@@ -2,8 +2,10 @@
2
2
  <div
3
3
  v-if="visibleColors.length > 0"
4
4
  class="ProductsColors__wrapper">
5
- <toggle-content label="Colours">
6
- <div class="ProductsColors__selected">
5
+ <toggle-content label="Colours" :toggleable="toggleable">
6
+ <div
7
+ v-if="displayMode === 'grid'"
8
+ class="ProductsColors__selected">
7
9
  <div
8
10
  v-for="color in selectedColors"
9
11
  :key="color._id"
@@ -21,7 +23,9 @@
21
23
  </products-link>
22
24
  </div>
23
25
  </div>
24
- <div class="ProductsColors__items">
26
+ <div
27
+ v-if="displayMode === 'grid'"
28
+ class="ProductsColors__items">
25
29
  <products-link
26
30
  v-for="color in visibleColors"
27
31
  :key="color._id"
@@ -39,6 +43,33 @@
39
43
  </div>
40
44
  </products-link>
41
45
  </div>
46
+ <div
47
+ v-else
48
+ class="ProductsColors__list">
49
+ <products-link
50
+ v-for="color in visibleColors"
51
+ :key="color._id"
52
+ :color="color.alias">
53
+ <div
54
+ class="ProductsColors__list-item"
55
+ :class="{
56
+ 'ProductsColors__list-item--selected': isSelectedColor(color)
57
+ }">
58
+ <div class="ProductsColors__list-checkbox">
59
+ <checked-icon :checked="isSelectedColor(color)" />
60
+ </div>
61
+ <div class="ProductsColors__list-thumb ProductsColors__color">
62
+ <product-color-image
63
+ v-if="canLoadImages"
64
+ :color="color"
65
+ class="ProductsColors__color-image" />
66
+ </div>
67
+ <div class="ProductsColors__list-label accu-filter-item">
68
+ {{ color.name }}
69
+ </div>
70
+ </div>
71
+ </products-link>
72
+ </div>
42
73
  <div
43
74
  v-if="shortListColorsLimit < colorsCount"
44
75
  @click="toggleDisplayAllColors()"
@@ -54,13 +85,26 @@ import { mapGetters } from 'vuex';
54
85
  import ToggleContent from '@lancom/shared/components/common/toggle-content';
55
86
  import ProductsLink from '@lancom/shared/components/products/products_link/products-link';
56
87
  import ProductColorImage from '@lancom/shared/components/product/product_color_image/product-color-image';
88
+ import CheckedIcon from '@lancom/shared/components/common/checked-icon';
57
89
 
58
90
  export default {
59
91
  name: 'ProductsColors',
60
92
  components: {
61
93
  ProductColorImage,
62
94
  ProductsLink,
63
- ToggleContent
95
+ ToggleContent,
96
+ CheckedIcon
97
+ },
98
+ props: {
99
+ toggleable: {
100
+ type: Boolean,
101
+ default: true
102
+ },
103
+ displayMode: {
104
+ type: String,
105
+ default: 'grid', // 'grid' or 'list'
106
+ validator: value => ['grid', 'list'].includes(value)
107
+ }
64
108
  },
65
109
  data() {
66
110
  return {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="ProductsMinimumQty__wrapper">
3
- <toggle-content label="Minimum Qty">
3
+ <toggle-content label="Minimum Qty" :toggleable="toggleable">
4
4
  <div
5
5
  v-for="minimumQtyRange in minimumQtyRanges"
6
6
  :key="minimumQtyRange.alias"
@@ -44,6 +44,10 @@ export default {
44
44
  selectedIconCircle: {
45
45
  type: Boolean,
46
46
  default: false
47
+ },
48
+ toggleable: {
49
+ type: Boolean,
50
+ default: true
47
51
  }
48
52
  },
49
53
  data() {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="ProductsProductionTime__wrapper">
3
- <toggle-content label="Production Time">
3
+ <toggle-content label="Production Time" :toggleable="toggleable">
4
4
  <div
5
5
  v-for="produnctionRange in produnctionTimesRanges"
6
6
  :key="produnctionRange.alias"
@@ -44,6 +44,10 @@ export default {
44
44
  selectedIconCircle: {
45
45
  type: Boolean,
46
46
  default: false
47
+ },
48
+ toggleable: {
49
+ type: Boolean,
50
+ default: true
47
51
  }
48
52
  },
49
53
  data() {
@@ -0,0 +1,59 @@
1
+ @import "@/assets/scss/variables";
2
+
3
+ .ProductsSelectedFilters {
4
+ &__wrapper {
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: space-between;
8
+ width: 100%;
9
+ }
10
+ &__title {
11
+ font-weight: 600;
12
+ font-size: 0.875rem;
13
+ color: var(--accu-text-dark);
14
+ margin-right: 0.5rem;
15
+ }
16
+ &__list {
17
+ display: flex;
18
+ flex-wrap: wrap;
19
+ gap: 8px;
20
+ align-items: center;
21
+ }
22
+ &__item {
23
+ display: inline-flex;
24
+ align-items: center;
25
+ gap: 0.25rem;
26
+ background: #EEF0FB;
27
+ color: var(--accu-primary);
28
+ padding: 0.25rem 0.75rem;
29
+ border-radius: 20px;
30
+ font-size: 0.8rem;
31
+ &-label {
32
+ font-weight: 600;
33
+ margin-right: 4px;
34
+ }
35
+ &-value {
36
+ margin-right: 8px;
37
+ }
38
+ &-remove {
39
+ cursor: pointer;
40
+ transition: color 0.2s;
41
+ &:hover {
42
+ color: darken($grey_1, 15%);
43
+ }
44
+ }
45
+ a {
46
+ display: flex;
47
+ align-items: center;
48
+ color: inherit;
49
+ text-decoration: none;
50
+ }
51
+ }
52
+ &__clear-all {
53
+ color: var(--accu-primary);
54
+ font-size: 0.875rem;
55
+ text-decoration: underline;
56
+ cursor: pointer;
57
+ margin-left: auto;
58
+ }
59
+ }
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <div
3
+ v-if="hasSelectedFilters"
4
+ class="ProductsSelectedFilters__wrapper">
5
+ <div class="ProductsSelectedFilters__list">
6
+ <div class="ProductsSelectedFilters__title">
7
+ Selected Filters:
8
+ </div>
9
+ <div
10
+ v-if="selectedBrand"
11
+ class="ProductsSelectedFilters__item">
12
+ <span class="ProductsSelectedFilters__item-label">Brands:</span>
13
+ <span class="ProductsSelectedFilters__item-value">{{ selectedBrand.name }}</span>
14
+ <products-link :brand="null">
15
+ <i class="icon-cancel ProductsSelectedFilters__item-remove"></i>
16
+ </products-link>
17
+ </div>
18
+ <div
19
+ v-if="selectedType"
20
+ class="ProductsSelectedFilters__item">
21
+ <span class="ProductsSelectedFilters__item-label">Product Type:</span>
22
+ <span class="ProductsSelectedFilters__item-value">{{ selectedType.name }}</span>
23
+ <products-link :type="null">
24
+ <i class="icon-cancel ProductsSelectedFilters__item-remove"></i>
25
+ </products-link>
26
+ </div>
27
+ <div
28
+ v-for="colorGroup in selectedColorGroups"
29
+ :key="`colorGroup-${colorGroup._id}`"
30
+ class="ProductsSelectedFilters__item">
31
+ <span class="ProductsSelectedFilters__item-label">Colour Groups:</span>
32
+ <span class="ProductsSelectedFilters__item-value">{{ colorGroup.name }}</span>
33
+ <products-link :color-group="colorGroup.alias">
34
+ <i class="icon-cancel ProductsSelectedFilters__item-remove"></i>
35
+ </products-link>
36
+ </div>
37
+ <div
38
+ v-for="color in selectedColors"
39
+ :key="`color-${color._id}`"
40
+ class="ProductsSelectedFilters__item">
41
+ <span class="ProductsSelectedFilters__item-label">Colours:</span>
42
+ <span class="ProductsSelectedFilters__item-value">{{ color.name }}</span>
43
+ <products-link :color="color.alias">
44
+ <i class="icon-cancel ProductsSelectedFilters__item-remove"></i>
45
+ </products-link>
46
+ </div>
47
+ <template v-for="attribute in attributes">
48
+ <div
49
+ v-for="option in getSelectedAttributeOptions(attribute)"
50
+ :key="`attr-${attribute._id}-${option._id}`"
51
+ class="ProductsSelectedFilters__item">
52
+ <span class="ProductsSelectedFilters__item-label">{{ attribute.name }}:</span>
53
+ <span class="ProductsSelectedFilters__item-value">{{ option.name }}</span>
54
+ <products-link :attributes="{ [attribute.alias]: option.alias }">
55
+ <i class="icon-cancel ProductsSelectedFilters__item-remove"></i>
56
+ </products-link>
57
+ </div>
58
+ </template>
59
+ <div
60
+ v-for="tag in selectedTagsList"
61
+ :key="`tag-${tag.alias}`"
62
+ class="ProductsSelectedFilters__item">
63
+ <span class="ProductsSelectedFilters__item-label">Tags:</span>
64
+ <span class="ProductsSelectedFilters__item-value">{{ tag.name }}</span>
65
+ <products-link :tags="tag.alias">
66
+ <i class="icon-cancel ProductsSelectedFilters__item-remove"></i>
67
+ </products-link>
68
+ </div>
69
+ </div>
70
+ <div
71
+ v-if="hasSelectedFilters"
72
+ class="ProductsSelectedFilters__clear-all"
73
+ @click="clearAllFilters">
74
+ Clear All
75
+ </div>
76
+ </div>
77
+ </template>
78
+
79
+ <script>
80
+ import ProductsLink from '@lancom/shared/components/products/products_link/products-link';
81
+ import selectedFiltersMixin from '@lancom/shared/mixins/products/selected-filters';
82
+
83
+ export default {
84
+ name: 'ProductsSelectedFilters',
85
+ components: {
86
+ ProductsLink
87
+ },
88
+ mixins: [selectedFiltersMixin],
89
+ methods: {
90
+ clearAllFilters() {
91
+ // Navigate to the base products route without any filters
92
+ const baseRoute = {
93
+ name: this.$route.name,
94
+ params: {
95
+ ...this.$route.params,
96
+ brand: undefined,
97
+ type: undefined
98
+ },
99
+ query: {}
100
+ };
101
+ this.$router.push(baseRoute);
102
+ }
103
+ }
104
+ };
105
+ </script>
106
+
107
+ <style lang="scss" scoped>
108
+ @import 'products-selected-filters';
109
+ </style>
@@ -2,7 +2,7 @@
2
2
  <div
3
3
  v-if="tags.length"
4
4
  class="ProductsTags__wrapper">
5
- <toggle-content label="Tags">
5
+ <toggle-content label="Tags" :toggleable="toggleable">
6
6
  <div class="ProductsTags__options">
7
7
  <div
8
8
  v-for="tag in tags"
@@ -42,6 +42,10 @@ export default {
42
42
  hasRemoveIcon: {
43
43
  type: Boolean,
44
44
  default: false
45
+ },
46
+ toggleable: {
47
+ type: Boolean,
48
+ default: true
45
49
  }
46
50
  },
47
51
  computed: {
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="ProductsTypes__wrapper">
3
- <toggle-content label="Product type">
3
+ <toggle-content label="Product type" :toggleable="toggleable">
4
4
  <div
5
5
  v-for="type in types"
6
6
  :key="type.alias"
@@ -56,6 +56,10 @@ export default {
56
56
  hasThumbs: {
57
57
  type: Boolean,
58
58
  default: true
59
+ },
60
+ toggleable: {
61
+ type: Boolean,
62
+ default: true
59
63
  }
60
64
  },
61
65
  computed: {
@@ -7,7 +7,13 @@
7
7
  &__content {
8
8
  overflow: hidden;
9
9
  @import "@lancom/shared/assets/scss/normalize";
10
+ p {
11
+ &:last-child {
12
+ margin: 0;
13
+ }
14
+ }
10
15
  }
16
+
11
17
  }
12
18
 
13
19
  .post-full-image {
@@ -61,6 +61,9 @@ const productPreview = {
61
61
  productCountries() {
62
62
  return this.countries.filter(c => this.product.countries?.includes(c._id));
63
63
  },
64
+ aggregateRating() {
65
+ return this.product?.aggregateRating || { value: 0, count: 0 };
66
+ },
64
67
  noMinimum() {
65
68
  return !(this.product.minimumPrintOrderQuantity > 1) && !(this.product.minimumOrderQuantity > 1);
66
69
  },
@@ -0,0 +1,49 @@
1
+ import { mapGetters } from 'vuex';
2
+
3
+ export default {
4
+ computed: {
5
+ ...mapGetters('products', ['brands', 'types', 'colorGroups', 'colors', 'attributes', 'tags']),
6
+
7
+ selectedBrand() {
8
+ const brandAlias = this.$route.params.brand || this.$route.query.brand;
9
+ return brandAlias ? this.brands.find(b => b.alias === brandAlias) : null;
10
+ },
11
+
12
+ selectedType() {
13
+ const typeAlias = this.$route.params.type || this.$route.query.type;
14
+ return typeAlias ? this.types.find(t => t.alias === typeAlias) : null;
15
+ },
16
+
17
+ selectedColorGroups() {
18
+ const colorGroupsAliases = (this.$route.query.colorGroups || '').split(',').filter(Boolean);
19
+ return this.colorGroups.filter(cg => colorGroupsAliases.includes(cg.alias));
20
+ },
21
+
22
+ selectedColors() {
23
+ const colorsAliases = (this.$route.query.colors || '').split(',').filter(Boolean);
24
+ return this.colors.filter(c => colorsAliases.includes(c.alias));
25
+ },
26
+
27
+ selectedTagsList() {
28
+ const tagsAliases = (this.$route.query.tags || '').split(',').filter(Boolean);
29
+ return this.tags.filter(t => tagsAliases.includes(t.alias));
30
+ },
31
+
32
+ hasSelectedFilters() {
33
+ return this.selectedBrand ||
34
+ this.selectedType ||
35
+ this.selectedColorGroups.length > 0 ||
36
+ this.selectedColors.length > 0 ||
37
+ this.selectedTagsList.length > 0 ||
38
+ this.attributes.some(attr => this.getSelectedAttributeOptions(attr).length > 0);
39
+ }
40
+ },
41
+
42
+ methods: {
43
+ getSelectedAttributeOptions(attribute) {
44
+ const selectedAliases = (this.$route.query[attribute.alias] || '').split(',').filter(Boolean);
45
+ const options = attribute.options || [];
46
+ return options.filter(option => selectedAliases.includes(option.alias));
47
+ }
48
+ }
49
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.460",
3
+ "version": "0.0.462",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {