@lancom/shared 0.0.326 → 0.0.328

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.
@@ -200,6 +200,18 @@ export default {
200
200
  removePage(id) {
201
201
  return _delete(`admin/pages/${id}`);
202
202
  },
203
+ fetchCategories(params) {
204
+ return _get('admin/categories', params);
205
+ },
206
+ fetchCategoryById(id) {
207
+ return _get(`admin/categories/${id}`);
208
+ },
209
+ saveCategory(category) {
210
+ return category._id ? _put(`admin/categories/${category._id}`, category) : _post('admin/categories', category);
211
+ },
212
+ removeCategory(id) {
213
+ return _delete(`admin/categories/${id}`);
214
+ },
203
215
  fetchPackages() {
204
216
  return _get('admin/packages');
205
217
  },
@@ -731,5 +743,8 @@ export default {
731
743
  },
732
744
  findResources(query) {
733
745
  return _get(`admin/order/resources?search=${query || ''}`);
734
- }
746
+ },
747
+ getReOrderReport(params) {
748
+ return _get(`admin/reports/re-order`, params);
749
+ },
735
750
  };
@@ -98,12 +98,24 @@ export function filterBigSize(simpleProducts) {
98
98
  // };
99
99
  // };
100
100
 
101
- export function generateProductsLink($route, data) {
102
- let { type, brand, text, sort } = data;
103
- type = (type || type === null) ? type : ($route && $route.params.type);
104
- brand = (brand || brand === null) ? brand : ($route && $route.params.brand);
105
- text = (text || text === '') ? text : ($route && $route.query.text);
106
- sort = (sort || sort === '') ? sort : ($route && $route.query.sort);
101
+ function getCategoryPathFromRoute(routeParams) {
102
+ const categories = [];
103
+ for (let i = 5; i >= 0; i--) {
104
+ const category = routeParams?.[`category${!i ? '' : i}`];
105
+ if (category) {
106
+ categories.push(category);
107
+ }
108
+ }
109
+ return categories.join('/');
110
+ }
111
+
112
+ export function generateProductsLink($route, data, skipParams = false) {
113
+ let { type, category, brand, text, sort } = data;
114
+ category = (category || category === null) ? generateCategoryPath(category) : getCategoryPathFromRoute($route?.params);
115
+ type = (type || type === null) ? type : ($route?.params?.type || $route?.query?.type);
116
+ brand = (brand || brand === null) ? brand : ($route?.params?.brand || $route?.query?.brand);
117
+ text = (text || text === '') ? text : $route?.query?.text;
118
+ sort = (sort || sort === '') ? sort : $route?.query?.sort;
107
119
 
108
120
  const params = [];
109
121
 
@@ -120,7 +132,8 @@ export function generateProductsLink($route, data) {
120
132
  }
121
133
 
122
134
  Object.keys({ ...(($route && $route.query) || {}), ...data }).forEach(key => {
123
- if (!['text', 'sort', 'page', 'type', 'brand'].includes(key)) {
135
+ const rootKeys = ['text', 'sort', 'page', 'category', 'type', 'brand'];
136
+ if (!rootKeys.includes(key)) {
124
137
  let items = ($route && $route.query[key]) ? $route.query[key].split(',') : [];
125
138
  items = (data[key] ? (items.includes(data[key]) ? items.filter(c => c !== data[key]) : [...items, data[key]]) : items).join(',');
126
139
  if (items) {
@@ -128,16 +141,48 @@ export function generateProductsLink($route, data) {
128
141
  }
129
142
  }
130
143
  });
144
+ if (category) {
145
+ if (brand) {
146
+ params.push(`brand=${brand}`);
147
+ }
148
+ if (type) {
149
+ params.push(`type=${type}`);
150
+ }
151
+ }
131
152
 
132
- const typeRoute = type ? `/${type}` : '';
133
- const brandRoute = brand ? `/brand/${brand}` : '';
153
+ const categoryRoute = category ? `/c/${category}` : '';
154
+ const typeRoute = (!category && type) ? `/${type}` : '';
155
+ const brandRoute = (!category && brand) ? `/brand/${brand}` : '';
134
156
 
135
- return `/products${typeRoute}${brandRoute}${params.length ? `?${params.join('&')}` : ''}`;
157
+ const baseRoute = categoryRoute || `/products${typeRoute}${brandRoute}`;
158
+ return `${baseRoute}${(params.length && !skipParams) ? `?${params.join('&')}` : ''}`;
159
+ }
160
+
161
+ export function generateCategoryPath(cat) {
162
+ let category = cat;
163
+ const categories = [];
164
+ while (category) {
165
+ categories.unshift(category.alias);
166
+ category = category.parent;
167
+ }
168
+ let baseLink = '';
169
+ if (categories.length) {
170
+ baseLink = `${categories.join('/')}`;
171
+ }
172
+ return baseLink;
136
173
  }
137
174
 
138
175
  export function generateProductLink(product, color, toEditor = false) {
139
176
  const baseLink = `/${product.brand?.alias}/${product.productType?.alias}/${product.alias}`;
140
- return `${toEditor ? `${baseLink}/editor` : baseLink}${color ? `?color=${color.alias}` : ''}`;
177
+ let link = toEditor ? `${baseLink}/editor` : baseLink;
178
+ if (product.useTaxonomyUrl) {
179
+ const categoryPath = generateCategoryPath(product.category);
180
+ if (categoryPath) {
181
+ const categoryBaseLink = `/c/${categoryPath}/${product.alias}/${product.SKU.toLowerCase()}`;
182
+ link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}/info.html`;
183
+ }
184
+ }
185
+ return `${link}${color ? `?color=${color.alias || color}` : ''}`;
141
186
  }
142
187
 
143
188
  export function getProductColorImages(product, color, skipDefault = false) {
@@ -5,20 +5,20 @@
5
5
  <validation-provider
6
6
  v-slot="{ errors }"
7
7
  tag="div"
8
- name="Full Name"
8
+ :name="namePrefix + 'Full Name'"
9
9
  rules="required"
10
10
  class="form-col col-half">
11
11
  <label
12
- for="fullName"
12
+ :for="'fullName'+uniqueKey"
13
13
  class="form-label">
14
14
  Full Name
15
15
  </label>
16
16
  <input
17
- id="fullName"
17
+ :id="'fullName'+uniqueKey"
18
18
  ref="fullName"
19
19
  v-model="address.fullName"
20
20
  placeholder="Full Name"
21
- name="fullName"
21
+ :name="'fullName'+uniqueKey"
22
22
  type="text"
23
23
  class="form-field labelless"
24
24
  :class="{
@@ -35,19 +35,19 @@
35
35
  <validation-provider
36
36
  v-slot="{ errors }"
37
37
  tag="div"
38
- name="company"
38
+ :name="namePrefix + 'Company'"
39
39
  class="form-col col-half">
40
40
  <label
41
- for="company"
41
+ :for="'company'+uniqueKey"
42
42
  class="form-label">
43
43
  Company Name
44
44
  </label>
45
45
  <input
46
- id="company"
46
+ :id="'company'+uniqueKey"
47
47
  ref="company"
48
48
  v-model="address.company"
49
49
  placeholder="Company Name"
50
- name="company"
50
+ :name="'company'+uniqueKey"
51
51
  type="text"
52
52
  class="form-field labelless"
53
53
  :class="{
@@ -66,20 +66,20 @@
66
66
  <validation-provider
67
67
  v-slot="{ errors }"
68
68
  tag="div"
69
- name="email"
69
+ :name="namePrefix + 'Email'"
70
70
  rules="required|email"
71
71
  class="form-col col-half">
72
72
  <label
73
- for="email"
73
+ :for="'email'+uniqueKey"
74
74
  class="form-label">
75
75
  Email
76
76
  </label>
77
77
  <input
78
- id="email"
78
+ :id="'email'+uniqueKey"
79
79
  ref="email"
80
80
  v-model="address.email"
81
81
  placeholder="Email"
82
- name="email"
82
+ :name="'email'+uniqueKey"
83
83
  type="email"
84
84
  class="form-field labelless"
85
85
  :class="{
@@ -96,20 +96,20 @@
96
96
  <validation-provider
97
97
  v-slot="{ errors }"
98
98
  tag="div"
99
- name="Phone"
99
+ :name="namePrefix + 'Phone'"
100
100
  rules="required|phone"
101
101
  class="form-col col-half">
102
102
  <label
103
- for="phone"
103
+ :for="'phone'+uniqueKey"
104
104
  class="form-label">
105
105
  Phone number
106
106
  </label>
107
107
  <input
108
- id="phone"
108
+ :id="'phone'+uniqueKey"
109
109
  ref="phone"
110
110
  v-model="address.phone"
111
111
  placeholder="Phone"
112
- name="phone"
112
+ :name="'phone'+uniqueKey"
113
113
  type="text"
114
114
  class="form-field labelless"
115
115
  :class="{
@@ -127,19 +127,19 @@
127
127
  <validation-provider
128
128
  v-slot="{ errors }"
129
129
  tag="div"
130
- name="Address line 1"
130
+ :name="namePrefix + 'Address line 1'"
131
131
  rules="required"
132
132
  class="form-row">
133
133
  <label
134
- for="addressLine1"
134
+ :for="'addressLine1'+uniqueKey"
135
135
  class="form-label">
136
136
  Address line 1
137
137
  </label>
138
138
  <input
139
- id="addressLine1"
139
+ :id="'addressLine1'+uniqueKey"
140
140
  ref="addressLine1"
141
141
  v-model="address.addressLine1"
142
- name="addressLine1"
142
+ :name="'addressLine1'+uniqueKey"
143
143
  type="text"
144
144
  class="form-field labelless"
145
145
  placeholder="Address line 1"
@@ -158,18 +158,18 @@
158
158
  <validation-provider
159
159
  v-slot="{ errors }"
160
160
  tag="div"
161
- name="Address line 2"
161
+ :name="namePrefix + 'Address line 2'"
162
162
  class="form-row">
163
163
  <label
164
- for="addressLine2"
164
+ :for="'addressLine2'+uniqueKey"
165
165
  class="form-label">
166
166
  Address line 2
167
167
  </label>
168
168
  <input
169
- id="addressLine2"
169
+ :id="'addressLine2'+uniqueKey"
170
170
  ref="addressLine2"
171
171
  v-model="address.addressLine2"
172
- name="addressLine2"
172
+ :name="'addressLine2'+uniqueKey"
173
173
  type="text"
174
174
  class="form-field labelless"
175
175
  placeholder="Address line 2"
@@ -191,20 +191,21 @@
191
191
  :suburb="address.suburb"
192
192
  :labelless="true"
193
193
  :required="true"
194
+ :name-prefix="namePrefix"
194
195
  placeholder="Suburb"
195
196
  @select="handleSuburbChange">
196
197
  </postcode-select>
197
198
  <div class="form-col col-half">
198
199
  <label
199
- for="country"
200
+ :for="'country'+uniqueKey"
200
201
  class="form-label">
201
202
  Country
202
203
  </label>
203
204
  <input
204
- id="country"
205
+ :id="'country'+uniqueKey"
205
206
  ref="country"
206
207
  :value="address.country === 'Australia' ? 'Only Australia' : address.country"
207
- name="country"
208
+ :name="'country'+uniqueKey"
208
209
  type="country"
209
210
  class="form-field filled labelless"
210
211
  disabled />
@@ -214,18 +215,18 @@
214
215
  v-if="!withoutAdditionalInfo"
215
216
  v-slot="{ errors }"
216
217
  tag="div"
217
- name="additionalInfo"
218
+ :name="namePrefix + 'Additional Info'"
218
219
  class="form-row">
219
220
  <label
220
- for="additionalInfo"
221
+ :for="'additionalInfo'+uniqueKey"
221
222
  class="form-label">
222
223
  Additional Info
223
224
  </label>
224
225
  <input
225
- id="additionalInfo"
226
+ :id="'additionalInfo'+uniqueKey"
226
227
  ref="additionalInfo"
227
228
  v-model="address.additionalInfo"
228
- name="additionalInfo"
229
+ :name="'additionalInfo'+uniqueKey"
229
230
  placeholder="Additional Info"
230
231
  type="text"
231
232
  class="form-field labelless"
@@ -253,6 +254,11 @@ export default {
253
254
  components: {
254
255
  PostcodeSelect
255
256
  },
257
+ data() {
258
+ return {
259
+ uniqueKey: Math.random()
260
+ };
261
+ },
256
262
  props: {
257
263
  address: {
258
264
  type: Object,
@@ -264,6 +270,10 @@ export default {
264
270
  withoutAdditionalInfo: {
265
271
  type: Boolean,
266
272
  default: false
273
+ },
274
+ namePrefix: {
275
+ type: String,
276
+ default: ''
267
277
  }
268
278
  },
269
279
  computed: {
@@ -25,6 +25,7 @@
25
25
  <address-form
26
26
  :address="order.billingAddress"
27
27
  :copy-from="order.shippingAddress"
28
+ name-prefix="Delivery "
28
29
  :without-additional-info="true" />
29
30
  </div>
30
31
  </div>
@@ -7,7 +7,7 @@
7
7
  right: 0;
8
8
  }
9
9
  &__card {
10
- min-height: 250px;
10
+ min-height: 400px;
11
11
  display: flex;
12
12
  align-items: center;
13
13
  justify-content: center;
@@ -4,7 +4,7 @@
4
4
  v-slot="{ errors }"
5
5
  tag="div"
6
6
  :rules="required ? 'required' : ''"
7
- name="Postcode"
7
+ :name="namePrefix + 'Postcode'"
8
8
  class="form-group">
9
9
  <label
10
10
  v-if="labelless"
@@ -55,6 +55,9 @@
55
55
  <input
56
56
  type="text"
57
57
  :value="value"
58
+ :id="'postcode'+uniqueKey"
59
+ :name="'postcode'+uniqueKey"
60
+ :required="required"
58
61
  :class="{ 'filled': value || selected }"
59
62
  class="form-hidden-validator form-field"
60
63
  style="display: none" />
@@ -120,10 +123,15 @@ export default {
120
123
  },
121
124
  suburb: {
122
125
  type: Object
126
+ },
127
+ namePrefix: {
128
+ type: String,
129
+ default: ''
123
130
  }
124
131
  },
125
132
  data() {
126
133
  return {
134
+ uniqueKey: Math.random(),
127
135
  selected: null,
128
136
  suburbs: [],
129
137
  options: [],
@@ -54,7 +54,7 @@ export default {
54
54
  return this.brands;
55
55
  },
56
56
  currentBrand() {
57
- return this.$route.params.brand;
57
+ return this.$route.params.brand || this.$route.query.brand;
58
58
  }
59
59
  }
60
60
  };
@@ -12,6 +12,9 @@ import { generateProductsLink } from '@lancom/shared/assets/js/utils/product';
12
12
  export default {
13
13
  name: 'ProductsLink',
14
14
  props: {
15
+ category: {
16
+ type: String
17
+ },
15
18
  type: {
16
19
  type: String
17
20
  },
@@ -37,6 +40,7 @@ export default {
37
40
  computed: {
38
41
  link() {
39
42
  const params = {
43
+ category: this.category,
40
44
  type: this.type,
41
45
  brand: this.brand,
42
46
  tags: this.tags,
@@ -8,9 +8,9 @@
8
8
  :class="{
9
9
  'ProductsTypes__item--active': type.alias === currentType
10
10
  }">
11
- <a
12
- :href="`/products${type.alias === currentType ? '' : `/${type.alias}`}`"
13
- class="ProductsTypes__more">
11
+ <products-link
12
+ class="ProductsTypes__more"
13
+ :type="type.alias === currentType ? null : type.alias">
14
14
  <checked-icon
15
15
  v-if="hasSelectedIcon"
16
16
  :checked="type.alias === currentType"
@@ -25,7 +25,7 @@
25
25
  v-else-if="hasThumbs"
26
26
  :class="`icon-${type.alias}`"></i>
27
27
  {{ type.name }}
28
- </a>
28
+ </products-link>
29
29
  </div>
30
30
  </toggle-content>
31
31
  </div>
@@ -35,12 +35,14 @@
35
35
  import { mapGetters } from 'vuex';
36
36
  import ToggleContent from '@lancom/shared/components/common/toggle-content';
37
37
  import CheckedIcon from '@lancom/shared/components/common/checked-icon';
38
+ import ProductsLink from '@lancom/shared/components/products/products_link/products-link';
38
39
 
39
40
  export default {
40
41
  name: 'ProductsTypes',
41
42
  components: {
42
43
  ToggleContent,
43
- CheckedIcon
44
+ CheckedIcon,
45
+ ProductsLink
44
46
  },
45
47
  props: {
46
48
  hasSelectedIcon: {
@@ -59,7 +61,7 @@ export default {
59
61
  computed: {
60
62
  ...mapGetters('products', ['types']),
61
63
  currentType() {
62
- return this.$route.params.type;
64
+ return this.$route.params.type || this.$route.query.type;
63
65
  }
64
66
  }
65
67
  };
@@ -98,27 +98,51 @@
98
98
  computed: {
99
99
  ...mapGetters('page', ['routeInfo']),
100
100
  ...mapGetters(['notificationBar', 'shop', 'country', 'currency']),
101
- ...mapGetters('products', ['brands', 'types', 'tags', 'loadError', 'count', 'products', 'minPrice', 'maxPrice']),
101
+ ...mapGetters('products', ['category', 'categories', 'brands', 'types', 'tags', 'loadError', 'count', 'products', 'minPrice', 'maxPrice']),
102
+ pageItemCanonical() {
103
+ return this.breadcrumbs[this.breadcrumbs.length - 1]?.to;
104
+ },
102
105
  currentBrand() {
103
106
  return this.findByRouteParam(this.brands, 'brand');
104
107
  },
105
108
  currentTag() {
106
- return this.findByRouteParam(this.tags, 'category');
109
+ return this.findByRouteParam(this.tags, 'tag');
107
110
  },
108
111
  currentProductType() {
109
112
  return this.findByRouteParam(this.types, 'type');
110
113
  },
114
+ currentCategory() {
115
+ return this.category;
116
+ },
111
117
  breadcrumbs() {
112
118
  const breadcrumbs = [{
113
119
  to: '/products',
114
120
  text: 'Products'
115
121
  }];
116
-
122
+ if (this.currentCategory) {
123
+ const categoriesBreadcrumbs = [];
124
+ let category = this.currentCategory;
125
+ console.log('category: ', category);
126
+ while (category) {
127
+ categoriesBreadcrumbs.unshift({
128
+ to: generateProductsLink(this.$route, {
129
+ type: null,
130
+ brand: null,
131
+ tag: null,
132
+ category
133
+ }, true),
134
+ text: category.name
135
+ });
136
+ category = category.parent;
137
+ }
138
+ breadcrumbs.push(...categoriesBreadcrumbs);
139
+ }
117
140
  if (this.currentProductType) {
118
141
  breadcrumbs.push({
119
142
  to: generateProductsLink(this.$route, {
120
143
  type: this.currentProductType.alias,
121
144
  brand: null,
145
+ tag: null,
122
146
  category: null
123
147
  }),
124
148
  text: this.currentProductType.name
@@ -129,8 +153,9 @@
129
153
  breadcrumbs.push({
130
154
  to: generateProductsLink(this.$route, {
131
155
  type: this.currentProductType?.alias,
132
- category: this.currentTag?.alias,
133
- brand: null
156
+ tag: this.currentTag?.alias,
157
+ brand: null,
158
+ category: null
134
159
  }),
135
160
  text: this.currentTag.name
136
161
  });
@@ -140,8 +165,9 @@
140
165
  breadcrumbs.push({
141
166
  to: generateProductsLink(this.$route, {
142
167
  type: this.currentProductType?.alias,
143
- category: this.currentTag?.alias,
144
- brand: this.currentBrand?.alias
168
+ tag: this.currentTag?.alias,
169
+ brand: this.currentBrand?.alias,
170
+ category: this.currentCategory?.alias
145
171
  }),
146
172
  text: this.currentBrand.name
147
173
  });
@@ -155,6 +181,9 @@
155
181
  }
156
182
 
157
183
  const items = ['Products'];
184
+ if (this.currentCategory) {
185
+ items.push(this.currentCategory.name);
186
+ }
158
187
  if (this.currentProductType) {
159
188
  items.push(this.currentProductType.name);
160
189
  }
@@ -304,7 +333,8 @@
304
333
  fillWithItemData(text = '') {
305
334
  text = this.currentBrand ? text.replace(/{{brandName}}/g, this.currentBrand.name) : text;
306
335
  text = this.currentProductType ? text.replace(/{{typeName}}/g, this.currentProductType.name) : text;
307
- text = this.currentTag ? text.replace(/{{categoryName}}/g, this.currentTag.name) : text;
336
+ text = this.currentTag ? text.replace(/{{tagName}}/g, this.currentTag.name) : text;
337
+ text = this.currentCategory ? text.replace(/{{categoryName}}/g, this.currentCategory.name) : text;
308
338
  return text;
309
339
  },
310
340
  async openAside() {
@@ -19,7 +19,7 @@ const metaInfo = {
19
19
  },
20
20
  methods: {
21
21
  getCanonical() {
22
- return this.$route.path === '/' ? '' : this.$route.path;
22
+ return this.$route.path === '/' ? '' : (this.pageItemCanonical || this.$route.path);
23
23
  },
24
24
  generateTitleFromRoute() {
25
25
  return this.$route.path
@@ -7,17 +7,17 @@ import { tax, staticLink, inRange } from '@lancom/shared/assets/js/utils/filters
7
7
  import { getProductLargeCover } from '@lancom/shared/assets/js/utils/colors';
8
8
  import metaInfo from '@lancom/shared/mixins/meta-info';
9
9
  import { STORE_CODES } from '@/constants/store';
10
-
10
+ import { generateProductLink, generateProductsLink } from '@lancom/shared/assets/js/utils/product';
11
11
  const { mapActions, mapMutations } = createNamespacedHelpers('product');
12
12
 
13
- export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
13
+ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
14
14
  mixins: [metaInfo],
15
15
  transition(to, from) {
16
16
  if (from && from.name === 'brand-tees-slug') { return 'slide-left-to-right'; };
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 }) {
20
+ async asyncData({ store, params, error, query, redirect }) {
21
21
  try {
22
22
  const { print, color, colour } = query;
23
23
  const { shop, country, currency } = store.getters;
@@ -29,10 +29,20 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
29
29
  print,
30
30
  defaultColor: color || colour
31
31
  };
32
+
32
33
  await store.dispatch('product/fetchProduct', data);
33
34
  await store.dispatch('product/fetchProductDetails', data);
34
35
 
35
36
  const product = store.getters['product/product'];
37
+
38
+ if ((product.useTaxonomyUrl && !params.sku) || (!product.useTaxonomyUrl && params.sku)) {
39
+ const redirectUrl = generateProductLink(product, data.defaultColor, isEditor);
40
+ return redirect(301, redirectUrl);
41
+ }
42
+
43
+ if (params.sku && params.sku !== product?.SKU?.toLowerCase()) {
44
+ return error({ statusCode: 404, message: 'Product not found' });
45
+ }
36
46
  if (IS_PRODUCT_PRESET_PRINT_PRICING) {
37
47
  const printType = product.printTypes[0];
38
48
  store.commit('product/setSelectedPrintType', printType);
@@ -69,6 +79,9 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
69
79
  pageItemImage() {
70
80
  return this.mainProductImageSrc;
71
81
  },
82
+ pageItemCanonical() {
83
+ return generateProductLink(this.product);
84
+ },
72
85
  hasImages() {
73
86
  return this.images.length > 0;
74
87
  },
@@ -181,7 +194,7 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
181
194
  }
182
195
  },
183
196
  fillBreadcrumbs() {
184
- this.breadcrumbs = [{
197
+ const breadcrumbs = [{
185
198
  to: '/products',
186
199
  text: 'Products'
187
200
  }, {
@@ -190,6 +203,19 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING) => ({
190
203
  }, {
191
204
  text: this.product.name
192
205
  }];
206
+ if (this.product.useTaxonomyUrl && this.product.category) {
207
+ const categoriesBreadcrumbs = [];
208
+ let category = this.product.category;
209
+ while (category) {
210
+ categoriesBreadcrumbs.unshift({
211
+ to: generateProductsLink(null, { category }, true),
212
+ text: category.name
213
+ });
214
+ category = category.parent;
215
+ }
216
+ breadcrumbs.splice(1, 1, ...categoriesBreadcrumbs);
217
+ }
218
+ this.breadcrumbs = breadcrumbs;
193
219
  }
194
220
  },
195
221
  getRoute() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.326",
3
+ "version": "0.0.328",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
package/routes/index.js CHANGED
@@ -1,5 +1,80 @@
1
1
  module.exports = function(routes, resolve) {
2
2
  return [{
3
+ name: 'product-taxonomy-view-level1',
4
+ path: '/c/:category/:slug/:sku/info.html',
5
+ component: resolve('@/pages/_brand/_type/_slug/index.vue'),
6
+ chunkName: 'pages/product/view/level1'
7
+ }, {
8
+ name: 'product-taxonomy-view-level2',
9
+ path: '/c/:category1/:category/:slug/:sku/info.html',
10
+ component: resolve('@/pages/_brand/_type/_slug/index.vue'),
11
+ chunkName: 'pages/product/view/level2'
12
+ }, {
13
+ name: 'product-taxonomy-view-level3',
14
+ path: '/c/:category2/:category1/:category/:slug/:sku/info.html',
15
+ component: resolve('@/pages/_brand/_type/_slug/index.vue'),
16
+ chunkName: 'pages/product/view/level3'
17
+ }, {
18
+ name: 'product-taxonomy-view-level4',
19
+ path: '/c/:category3/:category2/:category1/:category/:slug/:sku/info.html',
20
+ component: resolve('@/pages/_brand/_type/_slug/index.vue'),
21
+ chunkName: 'pages/product/view/level4'
22
+ }, {
23
+ name: 'product-taxonomy-view-level5',
24
+ path: '/c/:category4/:category3/:category2/:category1/:category/:slug/:sku/info.html',
25
+ component: resolve('@/pages/_brand/_type/_slug/index.vue'),
26
+ chunkName: 'pages/product/view/level5'
27
+ }, {
28
+ name: 'product-taxonomy-editor-level1',
29
+ path: '/c/:category/:slug/:sku.html',
30
+ component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
31
+ chunkName: 'pages/product/editor/level1'
32
+ }, {
33
+ name: 'product-taxonomy-editor-level2',
34
+ path: '/c/:category1/:category/:slug/:sku.html',
35
+ component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
36
+ chunkName: 'pages/product/editor/level2'
37
+ }, {
38
+ name: 'product-taxonomy-editor-level3',
39
+ path: '/c/:category2/:category1/:category/:slug/:sku.html',
40
+ component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
41
+ chunkName: 'pages/product/editor/level3'
42
+ }, {
43
+ name: 'product-taxonomy-editor-level4',
44
+ path: '/c/:category3/:category2/:category1/:category/:slug/:sku.html',
45
+ component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
46
+ chunkName: 'pages/product/editor/level4'
47
+ }, {
48
+ name: 'product-taxonomy-editor-level5',
49
+ path: '/c/:category4/:category3/:category2/:category1/:category/:slug/:sku.html',
50
+ component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
51
+ chunkName: 'pages/product/editor/level5'
52
+ }, {
53
+ name: 'products-taxonomy-catalog-level1',
54
+ path: '/c/:category',
55
+ component: resolve('@/pages/products/index.vue'),
56
+ chunkName: 'pages/products/catalog/level1'
57
+ }, {
58
+ name: 'products-taxonomy-catalog-level2',
59
+ path: '/c/:category1/:category',
60
+ component: resolve('@/pages/products/index.vue'),
61
+ chunkName: 'pages/products/catalog/level2'
62
+ }, {
63
+ name: 'products-taxonomy-catalog-level3',
64
+ path: '/c/:category2/:category1/:category',
65
+ component: resolve('@/pages/products/index.vue'),
66
+ chunkName: 'pages/products/catalog/level3'
67
+ }, {
68
+ name: 'products-taxonomy-catalog-level4',
69
+ path: '/c/:category3/:category2/:category1/:category',
70
+ component: resolve('@/pages/products/index.vue'),
71
+ chunkName: 'pages/products/catalog/level4'
72
+ }, {
73
+ name: 'products-taxonomy-catalog-level5',
74
+ path: '/c/:category4/:category3/:category2/:category1/:category',
75
+ component: resolve('@/pages/products/index.vue'),
76
+ chunkName: 'pages/products/catalog/level5'
77
+ }, {
3
78
  name: 'order-view',
4
79
  path: '/order/:token',
5
80
  component: resolve('@lancom/shared/pages/order/_token/index.vue'),
package/store/products.js CHANGED
@@ -10,6 +10,8 @@ export const state = () => ({
10
10
  types: [],
11
11
  colors: [],
12
12
  brands: [],
13
+ category: null,
14
+ categories: [],
13
15
  tags: [],
14
16
  attributes: [],
15
17
  loadError: null,
@@ -21,6 +23,8 @@ export const getters = {
21
23
  types: ({ types }) => types || [],
22
24
  colors: ({ colors }) => colors || [],
23
25
  brands: ({ brands }) => brands || [],
26
+ categories: ({ categories }) => categories || [],
27
+ category: ({ category }) => category,
24
28
  tags: ({ tags }) => tags || [],
25
29
  attributes: ({ attributes }) => attributes || [],
26
30
  page: ({ page }) => page,
@@ -46,7 +50,7 @@ export const actions = {
46
50
  commit('setLoadError', null);
47
51
  const {
48
52
  products, count, page, perPage, productTypes, colors, brands, tags, attributes,
49
- minPrice, maxPrice
53
+ categories, minPrice, maxPrice, category
50
54
  } = await api.fetchProducts(shop, params);
51
55
  commit('setProducts', products);
52
56
  commit('setPage', page);
@@ -55,6 +59,8 @@ export const actions = {
55
59
  commit('setTypes', productTypes);
56
60
  commit('setColors', colors);
57
61
  commit('setBrands', brands);
62
+ commit('setCategories', categories);
63
+ commit('setCategory', category);
58
64
  commit('setTags', tags);
59
65
  commit('setAttributes', attributes);
60
66
  commit('setMinPrice', minPrice);
@@ -104,6 +110,12 @@ export const mutations = {
104
110
  setBrands(state, brands) {
105
111
  state.brands = brands;
106
112
  },
113
+ setCategories(state, categories) {
114
+ state.categories = categories;
115
+ },
116
+ setCategory(state, category) {
117
+ state.category = category;
118
+ },
107
119
  setTags(state, tags) {
108
120
  state.tags = tags;
109
121
  },