@lancom/shared 0.0.398 → 0.0.400

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 (35) hide show
  1. package/assets/js/api/admin.js +3 -0
  2. package/assets/js/api/helpers.js +14 -12
  3. package/assets/js/utils/auth.js +13 -0
  4. package/components/checkout/cart/cart_entity/cart-entity.scss +8 -0
  5. package/components/checkout/cart/cart_entity/cart_coupon_free_products/cart-coupon-free-products.scss +10 -2
  6. package/components/checkout/cart/cart_entity/cart_coupon_free_products/cart-coupon-free-products.vue +18 -1
  7. package/components/checkout/cart/cart_entity/cart_coupon_free_products/cart_coupon_free_product/cart-coupon-free-product.scss +7 -1
  8. package/components/checkout/cart/cart_entity/cart_coupon_free_products/cart_coupon_free_product/cart-coupon-free-product.vue +10 -1
  9. package/components/common/coupon_select/coupon-select.scss +5 -0
  10. package/components/common/coupon_select/coupon-select.vue +18 -0
  11. package/components/customer/customer_coupons/customer-coupons.scss +33 -26
  12. package/components/customer/customer_coupons/customer-coupons.vue +17 -5
  13. package/components/faq/faq.vue +13 -0
  14. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products-kit-option-products.scss +18 -0
  15. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products-kit-option-products.vue +10 -1
  16. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products_kit_option_product/products-kit-option-product.scss +7 -5
  17. package/components/products_kit/products_kit/products_kit_options/products_kit_option/products_kit_option_products/products_kit_option_product/products-kit-option-product.vue +3 -0
  18. package/mixins/authenticated.js +7 -0
  19. package/mixins/meta-info.js +7 -1
  20. package/mixins/notAuthenticated.js +7 -0
  21. package/mixins/product-preview.js +12 -0
  22. package/nuxt.config.js +2 -1
  23. package/package.json +2 -1
  24. package/pages/contact.vue +12 -0
  25. package/pages/customer/coupons.vue +6 -4
  26. package/pages/customer/orders.vue +6 -4
  27. package/pages/customer/settings.vue +6 -4
  28. package/plugins/cache-headers.js +8 -10
  29. package/server-middleware/leads.js +2 -2
  30. package/store/auth.js +5 -5
  31. package/store/cart.js +7 -0
  32. package/store/index.js +12 -11
  33. package/store/productsKit.js +16 -6
  34. package/middleware/authenticated.js +0 -5
  35. package/middleware/notAuthenticated.js +0 -5
@@ -395,6 +395,9 @@ export default {
395
395
  removeCoupon(id) {
396
396
  return _delete(`admin/coupons/${id}`);
397
397
  },
398
+ removeCoupons(ids) {
399
+ return _post('admin/coupons/delete', { ids });
400
+ },
398
401
  fetchReviews() {
399
402
  return _get('admin/reviews');
400
403
  },
@@ -1,13 +1,12 @@
1
1
  import axios from 'axios';
2
- const Cookie = process.client ? require('js-cookie') : undefined;
2
+ import { getAuthToken, removeAuthToken } from '@lancom/shared/assets/js/utils/auth';
3
3
 
4
4
  const axiosApiInstance = axios.create();
5
5
 
6
6
  if (process.client) {
7
- console.log('error: ');
8
7
  axiosApiInstance.interceptors.request.use(
9
8
  config => {
10
- const auth = Cookie.get('auth');
9
+ const auth = getAuthToken();
11
10
  config.headers = {
12
11
  Authorization: `Bearer ${auth}`
13
12
  };
@@ -16,16 +15,19 @@ if (process.client) {
16
15
  error => {
17
16
  return Promise.reject(error);
18
17
  });
19
- axiosApiInstance.interceptors.response.use(
20
- response => response,
21
- error => {
22
- if (error.response.status === 401) {
23
- Cookie.remove('auth');
24
- window.location ='/';
25
- return;
18
+ axiosApiInstance.interceptors.response.use(
19
+ response => response,
20
+ error => {
21
+ if (error.response.status === 401) {
22
+ removeAuthToken();
23
+ const currentPath = window.location.pathname;
24
+ if (currentPath && currentPath !== '/') {
25
+ window.location = '/';
26
26
  }
27
- return Promise.reject(error);
28
- });
27
+ return;
28
+ }
29
+ return Promise.reject(error);
30
+ });
29
31
  }
30
32
 
31
33
  const API_URL = process.client ? process.env.API_URL : process.env.LOCAL_API_URL;
@@ -0,0 +1,13 @@
1
+ const AUTH_KEY = 'LANCOM_AUTH';
2
+
3
+ export function getAuthToken() {
4
+ return localStorage?.getItem(AUTH_KEY);
5
+ }
6
+
7
+ export function setAuthToken(token) {
8
+ return localStorage?.setItem(AUTH_KEY, token);
9
+ }
10
+
11
+ export function removeAuthToken() {
12
+ return localStorage?.removeItem(AUTH_KEY);
13
+ }
@@ -63,4 +63,12 @@
63
63
  border-top: 1px solid #F4F4F4;
64
64
  }
65
65
  }
66
+ &__free-products {
67
+ background-color: #FFFBEB;
68
+ color: #B45309;
69
+ border: 1px solid #FDE68A;
70
+ border-radius: 8px;
71
+ padding: 16px;
72
+ margin-top: 16px;
73
+ }
66
74
  }
@@ -2,10 +2,18 @@
2
2
 
3
3
  .CartCouponFreeProducts {
4
4
  &__label {
5
- font-size: 12px;
6
- font-weight: bold;
5
+ font-weight: 700;
6
+ font-size: 1.1em;
7
+ margin: 0 0 8px 0;
8
+ display: flex;
9
+ align-items: center;
10
+ gap: 12px;
7
11
  }
8
12
  &__product {
9
13
  margin-top: 5px;
10
14
  }
15
+ &__main-product {
16
+ padding-left: 36px;
17
+ font-size: 12px;
18
+ }
11
19
  }
@@ -1,7 +1,19 @@
1
1
  <template>
2
2
  <div class="CartCouponFreeProducts__wrapper">
3
3
  <div class="CartCouponFreeProducts__label">
4
- Free products
4
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-gift">
5
+ <polyline points="20 12 20 22 4 22 4 12"></polyline>
6
+ <rect x="2" y="7" width="20" height="5"></rect>
7
+ <line x1="12" y1="22" x2="12" y2="7"></line>
8
+ <path d="M12 7H7.5a2.5 2.5 0 0 1 0-5C11 2 12 7 12 7z"></path>
9
+ <path d="M12 7h4.5a2.5 2.5 0 0 0 0-5C13 2 12 7 12 7z"></path>
10
+ </svg>
11
+ <span>You've Unlocked Free Gifts!</span>
12
+ </div>
13
+ <div
14
+ v-if="entity.product"
15
+ class="CartCouponFreeProducts__main-product">
16
+ {{ entity.product.name }} x {{ qty }}
5
17
  </div>
6
18
  <cart-coupon-free-product
7
19
  v-for="freeProduct in freeProducts"
@@ -29,6 +41,11 @@ export default {
29
41
  type: Object,
30
42
  required: true
31
43
  }
44
+ },
45
+ computed: {
46
+ qty() {
47
+ return this.entity.simpleProducts?.reduce((qty, sp) => qty + (sp.amount || 0), 0) || 0;
48
+ }
32
49
  }
33
50
  };
34
51
  </script>
@@ -2,6 +2,12 @@
2
2
 
3
3
  .CartCouponFreeProduct {
4
4
  &__wrapper {
5
- font-size: 14px;
5
+ padding-left: 36px;
6
+ font-weight: bold;
7
+ margin-top: 4px;
8
+ font-size: 12px;
9
+ a {
10
+ color: #B45309;
11
+ }
6
12
  }
7
13
  }
@@ -1,10 +1,16 @@
1
1
  <template>
2
2
  <div class="CartCouponFreeProduct__wrapper">
3
- {{ freeProduct.name }} x {{ qty }}
3
+ <a
4
+ :href="productLink"
5
+ target="_blank">
6
+ FREE {{ freeProduct.name }} x {{ qty }}
7
+ </a>
4
8
  </div>
5
9
  </template>
6
10
 
7
11
  <script>
12
+ import { generateProductLink } from '@lancom/shared/assets/js/utils/product';
13
+
8
14
  export default {
9
15
  name: 'CartCouponFreeProduct',
10
16
  props: {
@@ -18,6 +24,9 @@ export default {
18
24
  }
19
25
  },
20
26
  computed: {
27
+ productLink() {
28
+ return generateProductLink(this.freeProduct);
29
+ },
21
30
  qty() {
22
31
  return this.entity.simpleProducts?.reduce((qty, sp) => qty + (sp.amount || 0), 0) || 0;
23
32
  }
@@ -1,4 +1,9 @@
1
1
  .CouponSelect {
2
+ &__wrapper {
3
+ a {
4
+ color: black
5
+ }
6
+ }
2
7
  &__clear {
3
8
  font-size: 13px;
4
9
  margin-left: 5px;
@@ -76,6 +76,14 @@
76
76
  class="lc_caption form-help is-danger">
77
77
  Invalid coupon: Min Order for Coupon: {{ value.minOrderValue | price(currency) }}
78
78
  </div>
79
+ <div v-if="hasQualifyingProducts" class="lc_caption">
80
+ <div v-if="noQualifyingProductsInCart">
81
+ There are no qualifying products in your cart. See here for more information: <a href="/faq/other#entity-684640df5ce4d102f04974d1">EOFY Beanie offer</a>
82
+ </div>
83
+ <div v-if="hasQualifyingProductsInCart">
84
+ The coupon has been applied to '{{ sumQualifyingProductsInCart }}' qualifying products. See here for more information: <a href="/faq/other#entity-684640df5ce4d102f04974d1">EOFY Beanie offer</a>
85
+ </div>
86
+ </div>
79
87
  </div>
80
88
  </div>
81
89
  </template>
@@ -111,6 +119,16 @@ export default {
111
119
  },
112
120
  computed: {
113
121
  ...mapGetters(['shop']),
122
+ ...mapGetters('cart', ['sumQualifyingProductsInCart']),
123
+ hasQualifyingProducts() {
124
+ return true;
125
+ },
126
+ noQualifyingProductsInCart() {
127
+ return this.sumQualifyingProductsInCart === 0;
128
+ },
129
+ hasQualifyingProductsInCart() {
130
+ return this.sumQualifyingProductsInCart > 0;
131
+ },
114
132
  isValidPricing() {
115
133
  return !this.value?.minOrderValue || this.pricing.coupon;
116
134
  },
@@ -1,33 +1,40 @@
1
1
  @import "@/assets/scss/variables";
2
2
 
3
- .CustomerCoupons__wrapper {
4
- .VueTables {
5
- &__table {
6
- width: 100%;
7
- border-collapse: collapse;
8
- th, td {
9
- padding: 10px;
10
- text-align: left;
11
- border-bottom: 1px solid $grey_2;
12
- font-size: 14px;
13
- &:nth-child(-n+3) {
14
- width: 220px;
3
+ .CustomerCoupons {
4
+ &__wrapper {
5
+ .VueTables {
6
+ &__table {
7
+ width: 100%;
8
+ border-collapse: collapse;
9
+ th, td {
10
+ padding: 10px;
11
+ text-align: left;
12
+ border-bottom: 1px solid $grey_2;
13
+ font-size: 14px;
14
+ &:nth-child(-n+3) {
15
+ width: 220px;
16
+ }
15
17
  }
16
- }
17
- th {
18
- background-color: $grey_3;
19
- font-weight: bold;
20
- }
21
- a {
22
- color: $black;
23
- text-decoration: none;
24
- transition: color 0.2s ease;
25
-
26
- &:hover {
27
- color: darken($black, 10%);
28
- text-decoration: underline;
18
+ th {
19
+ background-color: $grey_3;
20
+ font-weight: bold;
21
+ }
22
+ a {
23
+ color: $black;
24
+ text-decoration: none;
25
+ transition: color 0.2s ease;
26
+
27
+ &:hover {
28
+ color: darken($black, 10%);
29
+ text-decoration: underline;
30
+ }
29
31
  }
30
32
  }
31
33
  }
32
34
  }
33
- }
35
+ &__expired {
36
+ font-size: 11px;
37
+ color: grey;
38
+ margin-top: 5px;
39
+ }
40
+ }
@@ -6,6 +6,16 @@
6
6
  ref="table"
7
7
  :columns="columns"
8
8
  :options="options">
9
+ <template #name="{ row }">
10
+ <div>
11
+ <div>{{ row.name }}</div>
12
+ <div
13
+ v-if="row.expiredAt"
14
+ class="CustomerCoupons__expired">
15
+ <span>Expired:</span> {{ row.expiredAt | shortDate }}
16
+ </div>
17
+ </div>
18
+ </template>
9
19
  <template #value="{ row }">
10
20
  <div v-if="row.percentValue || row.value">
11
21
  {{ row.percentValue ? `${row.percentValue}%` : price(row.value) }}
@@ -33,21 +43,24 @@
33
43
  </client-only>
34
44
  </div>
35
45
  </template>
36
-
46
+
37
47
  <script>
38
48
  import { mapGetters } from 'vuex';
39
49
  import Vue from 'vue';
40
50
  import { ServerTable } from 'vue-tables-2';
51
+ import { _get } from '@lancom/shared/assets/js/api/helpers';
52
+ import { price, shortDate } from '@lancom/shared/assets/js/utils/filters';
41
53
  import CustomerCouponApply from './customer_coupon_apply/customer-coupon-apply';
42
54
  import CustomerCouponPrints from './customer_coupon_prints/customer-coupon-prints';
43
55
  import CustomerCouponProducts from './customer_coupon_products/customer-coupon-products';
44
- import { _get } from '@lancom/shared/assets/js/api/helpers';
45
- import { price } from '@lancom/shared/assets/js/utils/filters';
46
56
 
47
57
  Vue.use(ServerTable);
48
58
 
49
59
  export default {
50
60
  name: 'CustomerCoupons',
61
+ filters: {
62
+ shortDate
63
+ },
51
64
  components: {
52
65
  CustomerCouponApply,
53
66
  CustomerCouponPrints,
@@ -96,8 +109,7 @@ export default {
96
109
  }
97
110
  };
98
111
  </script>
99
-
112
+
100
113
  <style lang="scss">
101
114
  @import 'customer-coupons';
102
115
  </style>
103
-
@@ -36,6 +36,7 @@
36
36
  class="FAQ__group-content">
37
37
  <div
38
38
  v-for="entity in group.entities"
39
+ :id="`entity-${entity._id}`"
39
40
  :key="entity._id"
40
41
  :ref="entity._id"
41
42
  class="FAQ__entity">
@@ -64,6 +65,8 @@
64
65
  </template>
65
66
 
66
67
  <script>
68
+ import { scrollToElement } from '@lancom/shared/assets/js/utils/scroll';
69
+
67
70
  export default {
68
71
  name: 'FAQ',
69
72
  props: {
@@ -89,6 +92,12 @@ export default {
89
92
  openedEntities: groups.reduce((items, { entities }) => [...items, ...entities.map(({ _id }) => _id)], [])
90
93
  };
91
94
  },
95
+ mounted() {
96
+ const hash = window.location.hash;
97
+ if (hash.startsWith('#entity-')) {
98
+ scrollToElement(hash.substring(1), 200);
99
+ }
100
+ },
92
101
  methods: {
93
102
  toggleGroup({ type }) {
94
103
  const index = this.openedGroups.indexOf(type);
@@ -221,6 +230,10 @@ $types: delivery, general, other, printing, garments;
221
230
  color: $grey_1;
222
231
  overflow: hidden;
223
232
  @import "@lancom/shared/assets/scss/normalize";
233
+
234
+ a {
235
+ color: #2196f3;
236
+ }
224
237
  }
225
238
  }
226
239
  }
@@ -3,5 +3,23 @@
3
3
  .ProductsKitOptionProducts {
4
4
  &__wrapper {
5
5
  display: flex;
6
+ position: relative;
7
+ }
8
+ &__product {
9
+ margin: 3px;
10
+ z-index: 0;
11
+ }
12
+ &__loading {
13
+ position: absolute;
14
+ top: 0;
15
+ right: 0;
16
+ bottom: 0;
17
+ left: 0;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ background-color: white;
22
+ opacity: 0.6;
23
+ z-index: 1;
6
24
  }
7
25
  }
@@ -7,6 +7,11 @@
7
7
  :option="option"
8
8
  :product="product"
9
9
  class="ProductsKitOptionProducts__product" />
10
+ <div
11
+ v-if="isLoading"
12
+ class="ProductsKitOptionProducts__loading">
13
+ <spinner />
14
+ </div>
10
15
  </div>
11
16
  </template>
12
17
 
@@ -34,7 +39,11 @@ export default {
34
39
  'country',
35
40
  'currency',
36
41
  'stockCountry'
37
- ])
42
+ ]),
43
+ ...mapGetters('productsKit', ['loadingOptions']),
44
+ isLoading() {
45
+ return this.loadingOptions[this.option._id];
46
+ }
38
47
  },
39
48
  mounted() {
40
49
  if (this.option.required && this.option.products?.length === 1) {
@@ -2,9 +2,9 @@
2
2
 
3
3
  .ProductsKitOptionProduct {
4
4
  &__wrapper {
5
- border: 1px solid $gray;
5
+ border: 2px solid $gray;
6
6
  &--active {
7
- border: 1px solid $green;
7
+ border: 2px solid #aac145;
8
8
  }
9
9
  }
10
10
  &__preview {
@@ -42,21 +42,23 @@
42
42
  ::v-deep {
43
43
  .ProductPreview {
44
44
  &__link {
45
- height: 175px;
45
+ height: 175px !important;
46
46
  &-cover,
47
47
  &-cover-hover {
48
- height: 175px;
49
- top: 10px;
48
+ height: 175px !important;
49
+ top: 10px !important;
50
50
  }
51
51
  }
52
52
  &__sku {
53
53
  font-size: 13px;
54
+ text-decoration: underline;
54
55
  }
55
56
  &__name {
56
57
  font-size: 15px;
57
58
  line-height: 20px;
58
59
  margin-top: 1px;
59
60
  min-height: 35px;
61
+ text-decoration: underline;
60
62
  }
61
63
  &__prices-printed-price {
62
64
  font-size: 16px;
@@ -14,7 +14,10 @@
14
14
  :product="product"
15
15
  :to-editor="false"
16
16
  :visible-color-thumbs="false"
17
+ :visible-info="false"
18
+ :visible-brand="false"
17
19
  :visible-prices="!option.required"
20
+ link-target="_blank"
18
21
  @preview="onPreviewProduct($event)"
19
22
  @color="onProductSelectColor($event)" />
20
23
  </div>
@@ -0,0 +1,7 @@
1
+ export default {
2
+ mounted() {
3
+ if (process.client && !this.$store.state.auth.token) {
4
+ this.$router.push('/customer/signin');
5
+ }
6
+ }
7
+ };
@@ -64,6 +64,12 @@ const metaInfo = {
64
64
  href: `https://${process.env.HOST_NAME}${canonical}`
65
65
  }];
66
66
 
67
+ metaTags.push({
68
+ hid: 'og:url',
69
+ property: 'og:url',
70
+ content: `https://${process.env.HOST_NAME}${canonical}`
71
+ });
72
+
67
73
  if (hasQueryParams) {
68
74
  metaTags.push({
69
75
  hid: 'robots',
@@ -105,7 +111,7 @@ const metaInfo = {
105
111
  property: 'og:image',
106
112
  content: pageImageUrl
107
113
  });
108
-
114
+
109
115
  link.push({
110
116
  hid: 'preloadImage',
111
117
  rel: 'preload',
@@ -0,0 +1,7 @@
1
+ export default {
2
+ mounted() {
3
+ if (process.client && this.$store.state.auth.token) {
4
+ this.$router.push('/');
5
+ }
6
+ }
7
+ };
@@ -33,6 +33,18 @@ const productPreview = {
33
33
  visibleColorThumbs: {
34
34
  type: Boolean,
35
35
  default: true
36
+ },
37
+ visibleInfo: {
38
+ type: Boolean,
39
+ default: true
40
+ },
41
+ visibleBrand: {
42
+ type: Boolean,
43
+ default: true
44
+ },
45
+ linkTarget: {
46
+ type: String,
47
+ default: '_self'
36
48
  }
37
49
  },
38
50
  data() {
package/nuxt.config.js CHANGED
@@ -78,7 +78,8 @@ module.exports = (config, axios, { raygunClient, publicPath, productUrlToEditor
78
78
  '@nuxtjs/axios',
79
79
  '@nuxtjs/dotenv',
80
80
  '@nuxtjs/sitemap',
81
- '@/node_modules/@lancom/feed/lib/module'
81
+ '@/node_modules/@lancom/feed/lib/module',
82
+ 'nuxt-client-init-module'
82
83
  ],
83
84
  axios: {
84
85
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.398",
3
+ "version": "0.0.400",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
@@ -12,6 +12,7 @@
12
12
  "dependencies": {
13
13
  "basic-auth": "^2.0.1",
14
14
  "lodash.get": "^4.4.2",
15
+ "nuxt-client-init-module": "^0.3.0",
15
16
  "vue2-hammer": "^2.1.2"
16
17
  },
17
18
  "devDependencies": {
package/pages/contact.vue CHANGED
@@ -17,18 +17,30 @@
17
17
  </iframe>
18
18
  </div>
19
19
  </div>
20
+ <div
21
+ v-if="routeInfo && routeInfo.text"
22
+ class="Products__text">
23
+ <LazyHydrate never>
24
+ <static-page
25
+ :visible-title="false"
26
+ :item="routeInfo" />
27
+ </LazyHydrate>
28
+ </div>
20
29
  </div>
21
30
  </div>
22
31
  </template>
23
32
 
24
33
  <script>
25
34
  import { mapGetters } from 'vuex';
35
+ import LazyHydrate from 'vue-lazy-hydration';
26
36
  import metaInfo from '@lancom/shared/mixins/meta-info';
27
37
 
28
38
  export default {
29
39
  name: 'ContactPage',
30
40
  components: {
41
+ LazyHydrate,
31
42
  Breadcrumbs: () => import('@lancom/shared/components/common/breadcrumbs/breadcrumbs'),
43
+ StaticPage: () => import( '@lancom/shared/components/static_page/static-page'),
32
44
  ContactUs: () => import('@lancom/shared/components/asides/contact_us/contact-us')
33
45
  },
34
46
  mixins: [metaInfo],
@@ -2,23 +2,25 @@
2
2
  <div
3
3
  :data-aos="aosFadeRight"
4
4
  class="CustomerCouponsPage__wrapper">
5
- <customer-menu active-menu-item="coupons" />
6
- <customer-coupons :customer="user" />
5
+ <client-only>
6
+ <customer-menu active-menu-item="coupons" />
7
+ <customer-coupons :customer="user" />
8
+ </client-only>
7
9
  </div>
8
10
  </template>
9
11
 
10
12
  <script>
11
13
  import { mapGetters } from 'vuex';
12
14
  import metaInfo from '@lancom/shared/mixins/meta-info';
15
+ import authenticated from '@lancom/shared/mixins/authenticated';
13
16
 
14
17
  export default {
15
18
  name: 'CustomerCouponsPage',
16
- middleware: 'authenticated',
17
19
  components: {
18
20
  CustomerCoupons: () => import('@lancom/shared/components/customer/customer_coupons/customer-coupons'),
19
21
  CustomerMenu: () => import('@lancom/shared/components/customer/customer_navigation_menu/customer-navigation-menu')
20
22
  },
21
- mixins: [metaInfo],
23
+ mixins: [metaInfo, authenticated],
22
24
  computed: {
23
25
  ...mapGetters('auth', ['user'])
24
26
  }
@@ -2,23 +2,25 @@
2
2
  <div
3
3
  :data-aos="aosFadeRight"
4
4
  class="CustomerOrdersPage__wrapper">
5
- <customer-navigation-menu active-menu-item="orders" />
6
- <customer-orders :customer="user" />
5
+ <client-only>
6
+ <customer-navigation-menu active-menu-item="orders" />
7
+ <customer-orders :customer="user" />
8
+ </client-only>
7
9
  </div>
8
10
  </template>
9
11
 
10
12
  <script>
11
13
  import { mapGetters } from 'vuex';
12
14
  import metaInfo from '@lancom/shared/mixins/meta-info';
15
+ import authenticated from '@lancom/shared/mixins/authenticated';
13
16
 
14
17
  export default {
15
18
  name: 'CustomerOrdersPage',
16
- middleware: 'authenticated',
17
19
  components: {
18
20
  CustomerOrders: () => import('@lancom/shared/components/customer/customer_orders/customer-orders'),
19
21
  CustomerNavigationMenu: () => import('@lancom/shared/components/customer/customer_navigation_menu/customer-navigation-menu')
20
22
  },
21
- mixins: [metaInfo],
23
+ mixins: [metaInfo, authenticated],
22
24
  computed: {
23
25
  ...mapGetters('auth', ['user'])
24
26
  }
@@ -2,22 +2,24 @@
2
2
  <div
3
3
  :data-aos="aosFadeRight"
4
4
  class="CustomerSettingsPage__wrapper">
5
- <customer-navigation-menu active-menu-item="settings" />
6
- <customer-settings />
5
+ <client-only>
6
+ <customer-navigation-menu active-menu-item="settings" />
7
+ <customer-settings />
8
+ </client-only>
7
9
  </div>
8
10
  </template>
9
11
 
10
12
  <script>
11
13
  import metaInfo from '@lancom/shared/mixins/meta-info';
14
+ import authenticated from '@lancom/shared/mixins/authenticated';
12
15
 
13
16
  export default {
14
17
  name: 'CustomerCreatePage',
15
- middleware: 'authenticated',
16
18
  components: {
17
19
  CustomerNavigationMenu: () => import('@lancom/shared/components/customer/customer_navigation_menu/customer-navigation-menu'),
18
20
  CustomerSettings: () => import('@lancom/shared/components/pages/customer/settings/settings')
19
21
  },
20
- mixins: [metaInfo]
22
+ mixins: [metaInfo, authenticated]
21
23
  };
22
24
  </script>
23
25
 
@@ -3,15 +3,13 @@ module.exports = function (req, res, next) {
3
3
  return next();
4
4
  }
5
5
 
6
- if (!/auth\=/.test(req.headers?.cookie || '')) {
7
- const routes = [
8
- /^\/quotes\//,
9
- /\.xml$/
10
- ];
11
- const value = routes.some(route => route.test(`${req.url}`)) ? 'no-cache' : `max-age=${31536000}`;
12
- res?.setHeader('Cache-Control', value);
13
- } else {
14
- res?.setHeader('Cache-Control', 'no-cache');
15
- }
6
+ const routes = [
7
+ /^\/quotes\//,
8
+ /\.xml$/,
9
+ /\.csv$/
10
+ ];
11
+ const value = routes.some(route => route.test(`${req.url}`)) ? 'no-cache' : `max-age=${31536000}`;
12
+ res?.setHeader('Cache-Control', value);
13
+
16
14
  next();
17
15
  };
@@ -10,7 +10,7 @@ const LEAD_ADMIN_PASS = 'ladmin2sad$S';
10
10
  module.exports = async function (req, res, next) {
11
11
  if (req.url === '/lead/leads.csv') {
12
12
  const credentials = basicAuth(req);
13
-
13
+
14
14
  if (!credentials || credentials.name !== LEAD_ADMIN || credentials.pass !== LEAD_ADMIN_PASS) {
15
15
  res.setHeader('WWW-Authenticate', 'Basic realm="Access restricted"');
16
16
  res.statusCode = 401;
@@ -46,7 +46,7 @@ module.exports = async function (req, res, next) {
46
46
  // }, [])
47
47
  .map(lead => [
48
48
  lead.action,
49
- (new Date(lead.createdAt)).toISOString().replace(/\.[0-9]*Z/, '+00:00').replace('Z', '+00:00'),
49
+ lead.createdAt,
50
50
  lead.googleClickId,
51
51
  lead.hashedEmail,
52
52
  lead.hashedPhoneNumber,
package/store/auth.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import decode from 'jwt-decode';
2
2
  import api from '@lancom/shared/assets/js/api';
3
- const Cookie = process.client ? require('js-cookie') : undefined;
3
+ import { setAuthToken, removeAuthToken } from '@lancom/shared/assets/js/utils/auth';
4
4
 
5
5
  export const state = () => ({
6
6
  token: null,
@@ -30,22 +30,22 @@ export const actions = {
30
30
  const request = customer ? api.authCustomer(customer, shop._id) : api.admin.authUser(user);
31
31
  try {
32
32
  const { token, user, customer } = await request;
33
- Cookie.set('auth', token, { expires: 365 });
33
+ setAuthToken(token);
34
34
  this.$axios.defaults.headers.authorization = token;
35
35
  commit('AUTH_SUCCESS', token, customer || user);
36
36
  return customer || user;
37
37
  } catch (err) {
38
38
  commit('AUTH_ERROR', err);
39
- Cookie.remove('auth');
39
+ removeAuthToken();
40
40
  }
41
41
  },
42
42
  auth_logout({ commit }) {
43
43
  commit('AUTH_LOGOUT');
44
- Cookie.remove('auth');
44
+ removeAuthToken();
45
45
  window.location.href = '/';
46
46
  },
47
47
  update_user({ commit }, { token, user }) {
48
- Cookie.set('auth', token, { expires: 365 });
48
+ setAuthToken(token);
49
49
  this.$axios.defaults.headers.authorization = token;
50
50
  commit('AUTH_SUCCESS', token, user);
51
51
  }
package/store/cart.js CHANGED
@@ -95,6 +95,13 @@ export const getters = {
95
95
  ];
96
96
  }, []);
97
97
  },
98
+ sumQualifyingProductsInCart: ({ entities, coupon }) => {
99
+ return entities.reduce((sum, entity) => {
100
+ const amount = entity.simpleProducts?.reduce((count, sp) => count + +(sp.amount || 0), 0) || 0;
101
+ const freeProducts = getCouponFreeProducts(coupon, entity.product);
102
+ return freeProducts.length > 0 ? sum + amount : sum;
103
+ }, 0);
104
+ },
98
105
  coupon: ({ coupon }) => coupon,
99
106
  needToPickup: ({ needToPickup }) => needToPickup,
100
107
  needToPickupWithoutErrors: (state, { notValidProductsPickup }) => state.needToPickup && notValidProductsPickup?.length === 0,
package/store/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import api from '@lancom/shared/assets/js/api';
2
2
  import { saveStatePlugin, loadState } from '@lancom/shared/plugins/save-state';
3
+ import { getAuthToken } from '@lancom/shared/assets/js/utils/auth';
4
+ import { getShopCountrySettings } from '@lancom/shared/assets/js/utils/shop';
3
5
  import { MESSAGES, COUNTRIES_MESSAGES } from '@/messages';
4
6
  import { SETTINGS, COUNTRIES_SETTINGS } from '@/settings';
5
7
  const cookieparser = process.server ? require('cookieparser') : undefined;
6
8
  const CLOSED_NOTIFICATION = 'lancom-closed-notification-1.0';
7
- import { getShopCountrySettings } from '@lancom/shared/assets/js/utils/shop';
8
9
 
9
10
  export const state = () => ({
10
11
  googleClickId: null,
@@ -53,6 +54,16 @@ export const getters = {
53
54
  };
54
55
 
55
56
  export const actions = {
57
+ async nuxtClientInit({ commit }, context) {
58
+ try {
59
+ const auth = getAuthToken();
60
+ if (auth) {
61
+ await commit('auth/AUTH_SUCCESS', auth);
62
+ }
63
+ } catch (e) {
64
+ console.log('nuxtClientInit: ', e);
65
+ }
66
+ },
56
67
  async nuxtServerInit({ commit }, { req }) {
57
68
  const shop = await api.fetchShopByUrl(process.env.HOST_NAME);
58
69
  const menus = await api.fetchMenus(shop._id);
@@ -88,16 +99,6 @@ export const actions = {
88
99
  });
89
100
  }
90
101
  }
91
-
92
- // }
93
- try {
94
- if (req.headers.cookie) {
95
- const { auth } = cookieparser.parse(req.headers.cookie);
96
- if (auth) {
97
- commit('auth/AUTH_SUCCESS', auth);
98
- }
99
- }
100
- } catch (e) {}
101
102
  },
102
103
  async loadState({ dispatch, commit, state: { shop, currency, country, notificationBar } }, query) {
103
104
  const state = await loadState();
@@ -8,6 +8,7 @@ export const state = () => ({
8
8
  amount: 0,
9
9
  productsKit: null,
10
10
  loadError: null,
11
+ loadingOptions: {},
11
12
  selectedOptionsProducts: {},
12
13
  selectedOptionsSimpleProducts: {},
13
14
  selectedOptionsColors: {},
@@ -35,6 +36,7 @@ export const getters = {
35
36
  loadError: ({ loadError }) => loadError,
36
37
  amount: ({ amount }) => amount,
37
38
  productsKitPricing: ({ productsKitPricing }) => productsKitPricing,
39
+ loadingOptions: ({ loadingOptions }) => loadingOptions,
38
40
  selectedOptionsProducts: ({ selectedOptionsProducts }) => selectedOptionsProducts,
39
41
  selectedOptionsSimpleProducts: ({ selectedOptionsSimpleProducts }) => selectedOptionsSimpleProducts,
40
42
  selectedOptionsColors: ({ selectedOptionsColors }) => selectedOptionsColors,
@@ -97,12 +99,17 @@ export const actions = {
97
99
  },
98
100
  async selectOptionProduct({ commit }, { option, product, country, currency, stockCountry }) {
99
101
  const params = { country, currency, stockCountry };
100
- const simpleProducts = product ? (await api.fetchProductDetails(null, product.alias, params)) : [];
101
-
102
- commit('setSelectedOptionProduct', { option, product });
103
- commit('setSelectedOptionSimpleProducts', { option, simpleProducts });
104
- commit('setSelectedOptionColor', { option, color: null });
105
- commit('setSelectedOptionSize', { option, size: null });
102
+ try {
103
+ commit('setLoadingOption', { option, loading: true });
104
+ const simpleProducts = product ? (await api.fetchProductDetails(null, product.alias, params)) : [];
105
+ commit('setSelectedOptionProduct', { option, product });
106
+ commit('setSelectedOptionSimpleProducts', { option, simpleProducts });
107
+ commit('setSelectedOptionColor', { option, color: null });
108
+ commit('setSelectedOptionSize', { option, size: null });
109
+ } catch (e) {
110
+ } finally {
111
+ commit('setLoadingOption', { option, loading: false });
112
+ }
106
113
  },
107
114
  selectOptionColor({ commit }, { option, color }) {
108
115
  commit('setSelectedOptionColor', { option, color });
@@ -168,6 +175,9 @@ export const mutations = {
168
175
  setSelectedOptionSimpleProducts(state, { option, simpleProducts }) {
169
176
  Vue.set(state.selectedOptionsSimpleProducts, option._id, simpleProducts);
170
177
  },
178
+ setLoadingOption(state, { option, loading }) {
179
+ Vue.set(state.loadingOptions, option._id, loading);
180
+ },
171
181
  setLoadError(state, error) {
172
182
  state.loadError = error;
173
183
  },
@@ -1,5 +0,0 @@
1
- export default function ({ store, redirect }) {
2
- if (!store.state.auth.token) {
3
- return redirect('/customer/signin');
4
- }
5
- }
@@ -1,5 +0,0 @@
1
- export default function ({ store, redirect }) {
2
- if (store.state.auth.token) {
3
- return redirect('/');
4
- }
5
- }