@lancom/shared 0.0.394 → 0.0.396

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.
@@ -41,6 +41,9 @@ const api = {
41
41
  fetchCustomerCoupons(customer, shop) {
42
42
  return _get(`shop/${shop}/customer/${customer._id}/coupons`);
43
43
  },
44
+ fetchChildrenCategories(category, params) {
45
+ return _get(`category/${category}/children`, params);
46
+ },
44
47
  searchProducts(shop, text, save) {
45
48
  return _get(`shop/${shop}/products/search`, { text, save });
46
49
  },
@@ -47,7 +47,7 @@ const gtm = {
47
47
  currency: currency?.isoCode || 'AUD',
48
48
  items: (products || []).map(product => {
49
49
  return {
50
- id: product.SKU,
50
+ id: product.SKU,
51
51
  item_name: product.name,
52
52
  google_business_vertical: 'retail',
53
53
  price: product.minPrice
@@ -111,10 +111,10 @@ const gtm = {
111
111
 
112
112
  const { shippingAddress } = order;
113
113
  if (shippingAddress) {
114
- const { fullName, email, phone, city, state, postcode, country, addressLine1, addressLine2 } = shippingAddress;
114
+ const { fullName, email, phone, phoneE164, city, state, postcode, country, addressLine1, addressLine2 } = shippingAddress;
115
115
  const [first_name, last_name] = fullName.split(' ');
116
116
  event.email = email;
117
- event.phone_number= phone;
117
+ event.phone_number = phoneE164 || phone;
118
118
  event.address = {
119
119
  first_name,
120
120
  last_name,
@@ -0,0 +1,134 @@
1
+ /**
2
+ * @typedef {Object} PhoneNumberValidationResult
3
+ * @property {string} e164Number
4
+ * @property {boolean} isValid
5
+ * @property {boolean} supportsSms
6
+ */
7
+
8
+ /**
9
+ * Transforms a phone number to E.164 format if valid for the specified country.
10
+ * @param {string|null|undefined} originalInput
11
+ * @param {string} country
12
+ * @param {string} [postcode]
13
+ * @returns {PhoneNumberValidationResult}
14
+ */
15
+ export function formatNumberToE164(originalInput, country) {
16
+ const defaultFailureResult = {
17
+ e164Number: '',
18
+ isValid: false,
19
+ supportsSms: false
20
+ };
21
+
22
+ if (originalInput === null || originalInput === undefined) {
23
+ return defaultFailureResult;
24
+ }
25
+
26
+ const countryCodeMap = {
27
+ AU: '+61',
28
+ };
29
+
30
+ const normalizedCountry = country.toUpperCase();
31
+ const dialingCode = countryCodeMap[normalizedCountry];
32
+
33
+ let cleanedInput = originalInput.trim().replace(/\s+/g, '').replace(/[-()]/g, '');
34
+
35
+ if (cleanedInput === '') {
36
+ return defaultFailureResult;
37
+ }
38
+
39
+ if (normalizedCountry !== 'AU' || !dialingCode) {
40
+ return defaultFailureResult;
41
+ }
42
+
43
+ if (
44
+ cleanedInput.startsWith('1300') ||
45
+ (cleanedInput.startsWith('13') && cleanedInput.length > 2 && cleanedInput.length <= 6) ||
46
+ cleanedInput.startsWith('1800') ||
47
+ (cleanedInput.startsWith('180') && cleanedInput.length > 3 && cleanedInput.length <= 7)
48
+ ) {
49
+ return defaultFailureResult;
50
+ }
51
+
52
+ if (
53
+ cleanedInput.startsWith(dialingCode.substring(1)) &&
54
+ cleanedInput.length === dialingCode.length - 1 + 9
55
+ ) {
56
+ const numPartAfterCountryCode = cleanedInput.substring(dialingCode.length - 1);
57
+ const firstDigitNumPart = numPartAfterCountryCode.charAt(0);
58
+ if (
59
+ /^\d+$/.test(numPartAfterCountryCode) &&
60
+ numPartAfterCountryCode.length === 9 &&
61
+ ['2', '3', '4', '7', '8'].includes(firstDigitNumPart)
62
+ ) {
63
+ return {
64
+ e164Number: dialingCode + numPartAfterCountryCode,
65
+ isValid: true,
66
+ supportsSms: firstDigitNumPart === '4',
67
+ };
68
+ }
69
+ return defaultFailureResult;
70
+ }
71
+
72
+ if (cleanedInput.startsWith(dialingCode)) {
73
+ let numPartAfterDialingCodeRaw = cleanedInput.substring(dialingCode.length);
74
+ if (numPartAfterDialingCodeRaw.startsWith('0')) {
75
+ numPartAfterDialingCodeRaw = numPartAfterDialingCodeRaw.substring(1);
76
+ }
77
+ const firstDigitNumPartFinal = numPartAfterDialingCodeRaw.charAt(0);
78
+ if (
79
+ /^\d+$/.test(numPartAfterDialingCodeRaw) &&
80
+ numPartAfterDialingCodeRaw.length === 9 &&
81
+ ['2', '3', '4', '7', '8'].includes(firstDigitNumPartFinal)
82
+ ) {
83
+ return {
84
+ e164Number: dialingCode + numPartAfterDialingCodeRaw,
85
+ isValid: true,
86
+ supportsSms: firstDigitNumPartFinal === '4',
87
+ };
88
+ }
89
+ return defaultFailureResult;
90
+ }
91
+
92
+ if (cleanedInput.startsWith('04')) {
93
+ const mobilePart = cleanedInput.substring(2);
94
+ if (/^\d+$/.test(mobilePart) && mobilePart.length === 8) {
95
+ return {
96
+ e164Number: dialingCode + '4' + mobilePart,
97
+ isValid: true,
98
+ supportsSms: true,
99
+ };
100
+ }
101
+ return defaultFailureResult;
102
+ }
103
+
104
+ if (cleanedInput.startsWith('0') && cleanedInput.length === 10) {
105
+ const areaCode = cleanedInput.charAt(1);
106
+ const numPart = cleanedInput.substring(2);
107
+ if (
108
+ ['2', '3', '7', '8'].includes(areaCode) &&
109
+ /^\d+$/.test(numPart) &&
110
+ numPart.length === 8
111
+ ) {
112
+ return {
113
+ e164Number: dialingCode + areaCode + numPart,
114
+ isValid: true,
115
+ supportsSms: false,
116
+ };
117
+ }
118
+ return defaultFailureResult;
119
+ }
120
+
121
+ if (cleanedInput.length === 9 && /^\d+$/.test(cleanedInput)) {
122
+ const firstDigit = cleanedInput.charAt(0);
123
+ if (['2', '3', '4', '7', '8'].includes(firstDigit)) {
124
+ return {
125
+ e164Number: dialingCode + cleanedInput,
126
+ isValid: true,
127
+ supportsSms: firstDigit === '4',
128
+ };
129
+ }
130
+ return defaultFailureResult;
131
+ }
132
+
133
+ return defaultFailureResult;
134
+ }
@@ -187,7 +187,7 @@ export function generateProductLink(product, color, toEditor = false, size) {
187
187
  const categoryPath = generateCategoryPath(product.category);
188
188
  if (categoryPath) {
189
189
  const categoryBaseLink = `/c/${categoryPath}/${product.alias}/${product.SKU.toLowerCase()}`;
190
- link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}/info.html`;
190
+ link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}.html`;
191
191
  }
192
192
  }
193
193
  const query = [];
@@ -69,7 +69,7 @@
69
69
  <phone-input
70
70
  :labelless="false"
71
71
  :required="requiredPhone"
72
- v-model="form.phone" />
72
+ :item="form" />
73
73
  <validation-provider
74
74
  v-slot="{ errors }"
75
75
  tag="div"
@@ -68,7 +68,7 @@
68
68
  </validation-provider>
69
69
  <phone-input
70
70
  :labelless="false"
71
- v-model="form.phone" />
71
+ :item="form" />
72
72
  <validation-provider
73
73
  v-slot="{ errors }"
74
74
  tag="div"
@@ -93,7 +93,7 @@
93
93
  {{ errors[0] }}
94
94
  </span>
95
95
  </validation-provider>
96
- <phone-input v-model="address.phone" class="form-col col-half" />
96
+ <phone-input :item="address" class="form-col col-half" />
97
97
  </div>
98
98
  <validation-provider
99
99
  v-slot="{ errors }"
@@ -42,6 +42,14 @@
42
42
  Apple Pay
43
43
  </span>
44
44
  </label>
45
+ <label
46
+ class="form-label OrderPaymentInformation__checkbox"
47
+ @click="updatePaymentType('paypal')">
48
+ <checked-icon :checked="paymentMethod === 'paypal'" />
49
+ <span class="lc_regular12 lc__grey1 OrderPaymentInformation__checkbox-label">
50
+ Paypal
51
+ </span>
52
+ </label>
45
53
  </div>
46
54
  <div
47
55
  v-if="paymentMethod === 'deposit'"
@@ -80,6 +88,7 @@
80
88
  :key="paymentMethod"
81
89
  :google="paymentMethod === 'google'"
82
90
  :apple="paymentMethod === 'apple'"
91
+ :paypal="paymentMethod === 'paypal'"
83
92
  :amount="cartPricing.totalPrice"
84
93
  :order="orderData"
85
94
  @inited="initedCard">
@@ -17,6 +17,7 @@ import { mapGetters } from 'vuex';
17
17
  export default {
18
18
  name: 'PaymentCard',
19
19
  components: {
20
+ Paypal: () => import('./paypal/paypal'),
20
21
  Applepay: () => import('./applepay/applepay'),
21
22
  Googlepay: () => import('./googlepay/googlepay'),
22
23
  Pinpayment: () => import('./pinpayment/pinpayment'),
@@ -36,6 +37,9 @@ export default {
36
37
  },
37
38
  apple: {
38
39
  type: Boolean
40
+ },
41
+ paypal: {
42
+ type: Boolean
39
43
  }
40
44
  },
41
45
  computed: {
@@ -47,6 +51,9 @@ export default {
47
51
  if (this.apple) {
48
52
  return 'applepay';
49
53
  }
54
+ if (this.paypal) {
55
+ return 'paypal';
56
+ }
50
57
  return this.payment?.type || 'stripe-payment';
51
58
  }
52
59
  },
@@ -0,0 +1,8 @@
1
+ @import "@/assets/scss/ui_kit";
2
+ @import "@/assets/scss/variables";
3
+
4
+ .Payment {
5
+ &__wrapper {
6
+ font-weight: bold;
7
+ }
8
+ }
@@ -0,0 +1,16 @@
1
+ <template>
2
+ <div class="Payment__wrapper">
3
+ Temporarily Not Available
4
+ </div>
5
+ </template>
6
+
7
+ <script>
8
+ export default {
9
+ name: 'Paypal'
10
+ };
11
+ </script>
12
+
13
+
14
+ <style lang="scss" scoped>
15
+ @import 'paypal.scss';
16
+ </style>
@@ -1,5 +1,6 @@
1
1
  <template>
2
2
  <div>
3
+ <div>{{ item }}</div>
3
4
  <div class="form-row">
4
5
  <label
5
6
  v-if="labelless"
@@ -63,6 +64,7 @@
63
64
 
64
65
  <script>
65
66
  import Multiselect from 'vue-multiselect';
67
+ import { formatNumberToE164 } from '@lancom/shared/assets/js/utils/phone';
66
68
 
67
69
  export default {
68
70
  name: 'PhoneInput',
@@ -73,8 +75,9 @@ export default {
73
75
  codeCountry: {
74
76
  type: Object
75
77
  },
76
- value: {
77
- type: String
78
+ item: {
79
+ type: Object,
80
+ required: true
78
81
  },
79
82
  labelText: {
80
83
  type: String
@@ -86,6 +89,14 @@ export default {
86
89
  type: String,
87
90
  default: 'Phone'
88
91
  },
92
+ phoneProperty: {
93
+ type: String,
94
+ default: 'phone'
95
+ },
96
+ phoneE164Property: {
97
+ type: String,
98
+ default: 'phoneE164'
99
+ },
89
100
  copyFrom: {
90
101
  type: Boolean,
91
102
  default: false
@@ -120,16 +131,26 @@ export default {
120
131
  },
121
132
  model: {
122
133
  get() {
123
- return this.value; // (this.value || '').replace(/^\+[0-9]{2}/, '');
134
+ return this.item[this.phoneProperty];
124
135
  },
125
136
  set(phone) {
126
- // if (this.code === '+61' && phone?.length === 10 && phone[0] === '0') {
127
- // phone = phone.slice(1);
128
- // }
129
- // this.$emit('input', `${this.code}${phone}`);
137
+ this.$set(this.item, this.phoneProperty, phone);
138
+ this.updatePhoneE164();
139
+
130
140
  this.$emit('input', phone);
131
141
  }
132
142
  }
143
+ },
144
+ mounted() {
145
+ this.updatePhoneE164();
146
+ },
147
+ methods: {
148
+ updatePhoneE164() {
149
+ const code = this.codeCountry?.isoCode || this.codeCountry || 'AU';
150
+ const result = formatNumberToE164(this.model, code);
151
+ const e164Number = result?.isValid ? result.e164Number : null;
152
+ this.$set(this.item, this.phoneE164Property, e164Number);
153
+ }
133
154
  }
134
155
  };
135
156
  </script>
@@ -133,7 +133,7 @@
133
133
  <div class="row">
134
134
  <div class="col-6">
135
135
  <phone-input
136
- v-model="customer.phone"
136
+ :item="customer"
137
137
  label-text="Phone (optional)" />
138
138
  </div>
139
139
  <div class="col-6">
@@ -52,7 +52,7 @@
52
52
  </span>
53
53
  </validation-provider>
54
54
  <phone-input
55
- v-model="form.phone"
55
+ :item="form"
56
56
  class="form-col col-half" />
57
57
  </div>
58
58
  <validation-provider
@@ -41,7 +41,7 @@
41
41
  </span>
42
42
  </validation-provider>
43
43
  <phone-input
44
- v-model="form.phone"
44
+ :item="form"
45
45
  class="form-col col-half" />
46
46
  </div>
47
47
  <validation-provider
@@ -0,0 +1,65 @@
1
+ @import "@/assets/scss/variables";
2
+
3
+ .ChildrenCategories {
4
+ &__wrapper {
5
+ display: flex;
6
+ flex-wrap: wrap;
7
+ margin-top: 20px;
8
+ @media (max-width: $bp-small-max) {
9
+ justify-content: center;
10
+ }
11
+ }
12
+ }
13
+
14
+ .CategoryCard {
15
+ width: 270px;
16
+ margin: 0 0 15px 15px;
17
+ position: relative;
18
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
19
+ border-radius: 5px;
20
+ overflow: hidden;
21
+ background-color: white;
22
+ box-shadow: 0px 0px 0px rgba(75, 75, 75, 0.2);
23
+
24
+ &:hover {
25
+ transform: translateY(-5px);
26
+ box-shadow: 0px 5px 15px rgba(75, 75, 75, 0.2);
27
+ }
28
+
29
+ &__link {
30
+ display: block;
31
+ text-decoration: none;
32
+ color: inherit;
33
+ }
34
+
35
+ &__image-wrapper {
36
+ width: 100%;
37
+ height: 300px;
38
+ overflow: hidden;
39
+ position: relative;
40
+ }
41
+
42
+ &__image {
43
+ width: 100%;
44
+ height: 100%;
45
+ object-fit: cover;
46
+ transition: transform 0.3s ease;
47
+
48
+ &:hover {
49
+ transform: scale(1.05);
50
+ }
51
+ }
52
+
53
+ &__name {
54
+ padding: 15px;
55
+ font-size: 16px;
56
+ font-weight: 600;
57
+ color: $grey_1;
58
+ text-align: center;
59
+ background-color: white;
60
+ }
61
+
62
+ @media (max-width: $bp-extra-small-max) {
63
+ margin: 0 auto 25px auto;
64
+ }
65
+ }
@@ -0,0 +1,43 @@
1
+ <template>
2
+ <div class="ChildrenCategories__wrapper">
3
+ <div
4
+ v-for="category in childrenCategories"
5
+ :key="category._id"
6
+ class="CategoryCard">
7
+ <a
8
+ :href="generateProductsLink($route, { category }, true)"
9
+ class="CategoryCard__link">
10
+ <div class="CategoryCard__image-wrapper">
11
+ <img
12
+ v-if="category.image"
13
+ :src="category.image.medium"
14
+ :alt="category.name"
15
+ class="CategoryCard__image" />
16
+ </div>
17
+ <div class="CategoryCard__name">
18
+ {{ category.name }}
19
+ </div>
20
+ </a>
21
+ </div>
22
+ </div>
23
+ </template>
24
+
25
+ <script>
26
+ import { mapGetters } from 'vuex';
27
+ import { generateProductsLink } from '@lancom/shared/assets/js/utils/product';
28
+
29
+ export default {
30
+ name: 'ChildrenCategories',
31
+ computed: {
32
+ ...mapGetters('products', ['childrenCategories'])
33
+ },
34
+ methods: {
35
+ generateProductsLink
36
+ }
37
+ };
38
+ </script>
39
+
40
+ <style lang="scss" scoped>
41
+ @import 'children-categories';
42
+ </style>
43
+
@@ -37,7 +37,7 @@
37
37
  </validation-provider>
38
38
  <phone-input
39
39
  class="form-row"
40
- v-model="quote.address.phone" />
40
+ :item="quote.address" />
41
41
  <validation-provider
42
42
  v-slot="{ errors }"
43
43
  tag="div"
@@ -427,7 +427,7 @@ export default {
427
427
  }))
428
428
  };
429
429
  })
430
- }
430
+ }
431
431
  };
432
432
  const body = {
433
433
  recaptchaToken,
@@ -1,6 +1,6 @@
1
1
  async function googleLocalShoppingFeed(axios, config, availableStores, country) {
2
2
  const result = await googleShoppingFeed(axios, config, availableStores, country, false, { allowSameDayDelivery: true });
3
-
3
+
4
4
  const fields = [
5
5
  'g:availability',
6
6
  'g:id',
@@ -59,7 +59,7 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
59
59
  const description = `${product.description || product.fabricInfoShort || product.name || ''}`
60
60
  .replace(/&nbsp;/g, ' ')
61
61
  .replace(/&middot;/, '·');
62
-
62
+
63
63
  let link = `https://${config.HOST_NAME}${generateProductLink(product, sp.color, isEditor)}`;
64
64
  link = link.includes('?') ? `${link}&size=${sp.size?.shortName}` : `${link}?size=${sp.size?.shortName}`
65
65
  link = link.includes('?') ? `${link}&price=IT` : `${link}?price=IT`
@@ -186,7 +186,7 @@ async function googleShoppingFeed(axios, config, availableStores, country, isEdi
186
186
  ];
187
187
  }, [])
188
188
  };
189
-
189
+
190
190
  return {
191
191
  _declaration: { _attributes: { version: "1.0", encoding: "utf-8" } },
192
192
  rss: {
@@ -221,7 +221,7 @@ function generateProductLink(product, color, toEditor) {
221
221
  const categoryPath = generateCategoryPath(product.category);
222
222
  if (categoryPath) {
223
223
  const categoryBaseLink = `/c/${categoryPath}/${product.alias}/${product.SKU.toLowerCase()}`;
224
- link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}/info.html`;
224
+ link = toEditor ? `${categoryBaseLink}.html` : `${categoryBaseLink}.html`;
225
225
  }
226
226
  }
227
227
  return `${link}${color ? `?color=${color.alias || color}` : ''}`;
@@ -20,12 +20,16 @@
20
20
  <h1 class="Products__name">
21
21
  {{ routeName }}
22
22
  </h1>
23
- <div class="Products__filters">
23
+ <div
24
+ v-if="!visiblChildrenCategories"
25
+ class="Products__filters">
24
26
  <products-filters @open="openAside" />
25
27
  </div>
26
28
  </div>
27
29
  <div class="Products__content">
28
- <div class="Products__aside">
30
+ <div
31
+ v-if="!visiblChildrenCategories"
32
+ class="Products__aside">
29
33
  <breakpoint
30
34
  name="md"
31
35
  mode="up">
@@ -33,7 +37,10 @@
33
37
  </breakpoint>
34
38
  </div>
35
39
  <div class="Products__list">
36
- <products-catalog class="Products__catalog" />
40
+ <children-categories v-if="visiblChildrenCategories" />
41
+ <products-catalog
42
+ v-else
43
+ class="Products__catalog" />
37
44
  </div>
38
45
  </div>
39
46
  <div
@@ -57,21 +64,22 @@
57
64
 
58
65
  <script>
59
66
  import debounce from 'lodash.debounce';
67
+ import LazyHydrate from 'vue-lazy-hydration';
60
68
  import { mapActions, mapGetters, mapMutations } from 'vuex';
61
69
  import gtm from '@lancom/shared/assets/js/utils/gtm';
62
70
  import metaInfo from '@lancom/shared/mixins/meta-info';
63
71
  import { generateProductsLink, generateProductLink } from '@lancom/shared/assets/js/utils/product';
64
72
  import { getProductLargeCover, getProductMediumCover } from '@lancom/shared/assets/js/utils/colors';
65
- import LazyHydrate from 'vue-lazy-hydration';
66
- import TheNavbar from '@/components/the_navbar/the-navbar';
67
73
  import TheChangesSavedIndicator from '@lancom/shared/components/the_changes_saved_indicator/the-changes-saved-indicator';
68
74
  import Breadcrumbs from '@lancom/shared/components/common/breadcrumbs/breadcrumbs';
69
- import TheFooter from '@/components/the_footer/the-footer';
75
+ import StaticPage from '@lancom/shared/components/static_page/static-page';
70
76
  import TheAside from '@lancom/shared/components/the_aside/the-aside';
71
- import ProductsAside from '@/components/products/products_aside/products-aside';
72
77
  import ProductsFilters from '@lancom/shared/components/products/products_filters/products-filters';
78
+ import ChildrenCategories from '@lancom/shared/components/products/children_categories/children-categories';
79
+ import ProductsAside from '@/components/products/products_aside/products-aside';
80
+ import TheNavbar from '@/components/the_navbar/the-navbar';
81
+ import TheFooter from '@/components/the_footer/the-footer';
73
82
  import ProductsCatalog from '@/components/products/products_catalog/products-catalog';
74
- import StaticPage from '@lancom/shared/components/static_page/static-page';
75
83
  import Error from './error';
76
84
 
77
85
  export default {
@@ -85,6 +93,7 @@
85
93
  ProductsAside,
86
94
  ProductsFilters,
87
95
  ProductsCatalog,
96
+ ChildrenCategories,
88
97
  Error,
89
98
  StaticPage,
90
99
  TheChangesSavedIndicator
@@ -93,6 +102,11 @@
93
102
  middleware: ['page-info'],
94
103
  async fetch() {
95
104
  await this.loadProducts();
105
+
106
+ if (this.page === 1 && this.products.length === 0) {
107
+ await this.fetchChildrenCategories(this.currentCategory);
108
+ }
109
+
96
110
  if (process.server) {
97
111
  this.$root.context?.res?.setHeader('Cache-Control', `max-age=${86400}`);;
98
112
  }
@@ -100,7 +114,10 @@
100
114
  computed: {
101
115
  ...mapGetters('page', ['routeInfo']),
102
116
  ...mapGetters(['notificationBar', 'shop', 'country', 'currency']),
103
- ...mapGetters('products', ['category', 'categories', 'brands', 'types', 'tags', 'loadError', 'count', 'products', 'minPrice', 'maxPrice']),
117
+ ...mapGetters('products', ['category', 'childrenCategories', 'categories', 'brands', 'types', 'tags', 'loadError', 'count', 'products', 'minPrice', 'maxPrice', 'page']),
118
+ visiblChildrenCategories() {
119
+ return this.page === 1 && this.products.length === 0 && this.childrenCategories.length > 0;
120
+ },
104
121
  pageItemCanonical() {
105
122
  const page = +this.$route.query.page;
106
123
  return `${this.breadcrumbs[this.breadcrumbs.length - 1]?.to}${page > 1 ? `?page=${page}` : ''}`;
@@ -302,7 +319,8 @@
302
319
  'loadState'
303
320
  ]),
304
321
  ...mapActions('products', [
305
- 'fetchProducts'
322
+ 'fetchProducts',
323
+ 'fetchChildrenCategories'
306
324
  ]),
307
325
  ...mapMutations('products', [
308
326
  'setPlaceholder'
package/nuxt.config.js CHANGED
@@ -38,6 +38,7 @@ module.exports = (config, axios, { raygunClient, publicPath, productUrlToEditor
38
38
  '/checkout/**',
39
39
  '/customer/**'
40
40
  ],
41
+ cacheTime: 10,
41
42
  routes: async () => {
42
43
  const { data } = await axios.get(`${config.LOCAL_API_URL}/feed/sitemap?host=${config.HOST_NAME}&toEditor=${!!productUrlToEditor}`);
43
44
  return [
@@ -137,7 +138,7 @@ module.exports = (config, axios, { raygunClient, publicPath, productUrlToEditor
137
138
  router: {
138
139
  extendRoutes(routes, resolve) {
139
140
  return [
140
- ...sharedRoutes(routes, resolve),
141
+ ...sharedRoutes(routes, resolve, process.env),
141
142
  ...routes,
142
143
  ];
143
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.394",
3
+ "version": "0.0.396",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
package/routes/index.js CHANGED
@@ -1,52 +1,55 @@
1
- module.exports = function(routes, resolve) {
1
+ module.exports = function(routes, resolve, config) {
2
+ const PRODUCT_LINK_EXTENSION = config?.PRODUCT_LINK_EXTENSION || '/info.html';
3
+ const PRODUCT_EDITOR_LINK_EXTENSION = config?.PRODUCT_EDITOR_LINK_EXTENSION || '.html';
4
+
2
5
  return [{
3
6
  name: 'product-taxonomy-view-level1',
4
- path: '/c/:category/:slug/:sku/info.html',
7
+ path: `/c/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
5
8
  component: resolve('@/pages/_brand/_type/_slug/index.vue'),
6
9
  chunkName: 'pages/product/view/level1'
7
10
  }, {
8
11
  name: 'product-taxonomy-view-level2',
9
- path: '/c/:category1/:category/:slug/:sku/info.html',
12
+ path: `/c/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
10
13
  component: resolve('@/pages/_brand/_type/_slug/index.vue'),
11
14
  chunkName: 'pages/product/view/level2'
12
15
  }, {
13
16
  name: 'product-taxonomy-view-level3',
14
- path: '/c/:category2/:category1/:category/:slug/:sku/info.html',
17
+ path: `/c/:category2/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
15
18
  component: resolve('@/pages/_brand/_type/_slug/index.vue'),
16
19
  chunkName: 'pages/product/view/level3'
17
20
  }, {
18
21
  name: 'product-taxonomy-view-level4',
19
- path: '/c/:category3/:category2/:category1/:category/:slug/:sku/info.html',
22
+ path: `/c/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
20
23
  component: resolve('@/pages/_brand/_type/_slug/index.vue'),
21
24
  chunkName: 'pages/product/view/level4'
22
25
  }, {
23
26
  name: 'product-taxonomy-view-level5',
24
- path: '/c/:category4/:category3/:category2/:category1/:category/:slug/:sku/info.html',
27
+ path: `/c/:category4/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_LINK_EXTENSION}`,
25
28
  component: resolve('@/pages/_brand/_type/_slug/index.vue'),
26
29
  chunkName: 'pages/product/view/level5'
27
30
  }, {
28
31
  name: 'product-taxonomy-editor-level1',
29
- path: '/c/:category/:slug/:sku.html',
32
+ path: `/c/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
30
33
  component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
31
34
  chunkName: 'pages/product/editor/level1'
32
35
  }, {
33
36
  name: 'product-taxonomy-editor-level2',
34
- path: '/c/:category1/:category/:slug/:sku.html',
37
+ path: `/c/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
35
38
  component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
36
39
  chunkName: 'pages/product/editor/level2'
37
40
  }, {
38
41
  name: 'product-taxonomy-editor-level3',
39
- path: '/c/:category2/:category1/:category/:slug/:sku.html',
42
+ path: `/c/:category2/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
40
43
  component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
41
44
  chunkName: 'pages/product/editor/level3'
42
45
  }, {
43
46
  name: 'product-taxonomy-editor-level4',
44
- path: '/c/:category3/:category2/:category1/:category/:slug/:sku.html',
47
+ path: `/c/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
45
48
  component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
46
49
  chunkName: 'pages/product/editor/level4'
47
50
  }, {
48
51
  name: 'product-taxonomy-editor-level5',
49
- path: '/c/:category4/:category3/:category2/:category1/:category/:slug/:sku.html',
52
+ path: `/c/:category4/:category3/:category2/:category1/:category/:slug/:sku${PRODUCT_EDITOR_LINK_EXTENSION}`,
50
53
  component: resolve('@/pages/_brand/_type/_slug/editor/index.vue'),
51
54
  chunkName: 'pages/product/editor/level5'
52
55
  }, {
package/store/products.js CHANGED
@@ -12,6 +12,7 @@ export const state = () => ({
12
12
  brands: [],
13
13
  category: null,
14
14
  categories: [],
15
+ childrenCategories: [],
15
16
  tags: [],
16
17
  attributes: [],
17
18
  loadError: null,
@@ -25,6 +26,7 @@ export const getters = {
25
26
  brands: ({ brands }) => brands || [],
26
27
  categories: ({ categories }) => categories || [],
27
28
  category: ({ category }) => category,
29
+ childrenCategories: ({ childrenCategories }) => childrenCategories,
28
30
  tags: ({ tags }) => tags || [],
29
31
  attributes: ({ attributes }) => attributes || [],
30
32
  page: ({ page }) => page,
@@ -79,10 +81,22 @@ export const actions = {
79
81
  });
80
82
  throw e;
81
83
  }
84
+ },
85
+ async fetchChildrenCategories({ commit }, category) {
86
+ try {
87
+ const categories = await api.fetchChildrenCategories(category?._id);
88
+ commit('setChildrenCategories', categories);
89
+ return categories;
90
+ } catch (e) {
91
+ console.log(e);
92
+ }
82
93
  }
83
94
  };
84
95
 
85
96
  export const mutations = {
97
+ setChildrenCategories(state, categories) {
98
+ state.childrenCategories = categories;
99
+ },
86
100
  setPlaceholder(state, placeholder) {
87
101
  state.placeholder = placeholder;
88
102
  },