@lancom/shared 0.0.347 → 0.0.349

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.
@@ -0,0 +1,41 @@
1
+ @import "@/assets/scss/variables";
2
+
3
+ .ClientSettingsStockCountry {
4
+ &__wrapper {
5
+ display: flex;
6
+ }
7
+ &__field-item {
8
+ font-size: 12px;
9
+ cursor: pointer;
10
+ border-bottom: 1px dashed grey;
11
+ color: grey;
12
+ margin: 0 3px;
13
+ &--active {
14
+ border-bottom: 1px dashed black;
15
+ color: black;
16
+ }
17
+ }
18
+ &__countries {
19
+ display: flex;
20
+ position: absolute;
21
+ top: 280px;
22
+ left: 0px;
23
+ right: 0px;
24
+ justify-content: center;
25
+ z-index: 999;
26
+ }
27
+ &__country {
28
+ width: 32px;
29
+ height: 26px;
30
+ margin: 0 3px;
31
+ background-repeat: no-repeat;
32
+ background-position: center center;
33
+ background-size: contain;
34
+ cursor: pointer;
35
+ border: 2px solid rgba(189, 189, 189, 0);
36
+ border-radius: 3px;
37
+ &--active {
38
+ border: 2px solid $green;
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <div class="ClientSettingsStockCountry__wrapper">
3
+ <div
4
+ v-for="country in countries"
5
+ :key="country._id"
6
+ class="ClientSettingsStockCountry__country"
7
+ :style="{
8
+ 'background-image': `url(${country.icon})`
9
+ }"
10
+ :class="{
11
+ 'ClientSettingsStockCountry__country--active': country === checkedStockCountry
12
+ }"
13
+ @click="selectStockCountry(country)">
14
+ </div>
15
+ </div>
16
+ </template>
17
+
18
+ <script>
19
+ import { mapGetters } from 'vuex';
20
+
21
+ const Cookie = process.client ? require('js-cookie') : undefined;
22
+
23
+ export default {
24
+ name: 'ClientSettingsStockCountry',
25
+ computed: {
26
+ ...mapGetters([
27
+ 'stockCountry',
28
+ 'countries'
29
+ ]),
30
+ worldCountry() {
31
+ return this.countries.find(c => c.isoCode === 'world');
32
+ },
33
+ isExistsWorldCountry() {
34
+ return !!this.worldCountry;
35
+ },
36
+ checkedStockCountry() {
37
+ const country = this.countries.find(c => c._id === this.stockCountry?._id);
38
+ return country || (!this.stockCountry && this.worldCountry);
39
+ }
40
+ },
41
+ mounted() {
42
+ if (this.stockCountry) {
43
+ this.saveStockCountry(this.stockCountry);
44
+ }
45
+ },
46
+ methods: {
47
+ selectStockCountry(stockCountry) {
48
+ this.saveStockCountry(stockCountry);
49
+ window.location.reload();
50
+ },
51
+ saveStockCountry(stockCountry) {
52
+ const value = (stockCountry && stockCountry.isoCode !== 'world') ? stockCountry?._id : null
53
+ Cookie.set('stockCountry', value, { expires: 365 });
54
+ }
55
+ }
56
+ };
57
+ </script>
58
+
59
+ <style lang="scss">
60
+ @import 'client-settings-stock-country';
61
+ </style>
62
+
@@ -6,6 +6,11 @@
6
6
  @click="handleClick"
7
7
  @mouseover="$emit('option-mouseover', option)"
8
8
  @mouseleave="$emit('option-mouseleave', option)">
9
+ <div
10
+ v-if="option.suboptions.length > 1"
11
+ style="margin-right: -13px;">
12
+ <i class="icon-angle-down"></i>
13
+ </div>
9
14
  <div
10
15
  v-if="option.printArea"
11
16
  class="EditorPrintAreaOption__icon"
@@ -30,7 +30,7 @@
30
30
  <div
31
31
  :class="{ active: currentTab === 'layers' }"
32
32
  class="MobileEditorProductDetails__menu-action"
33
- @click="showLayers">
33
+ @click="show('layers')">
34
34
  <i class="icon-edit MobileEditorProductDetails__menu-action-icon"></i>
35
35
  <span class="MobileEditorProductDetails__menu-action-label">
36
36
  Text & Art
@@ -122,13 +122,15 @@ export default {
122
122
  window.addEventListener('resize', this.updateMobileMenuPositionWithThrottle);
123
123
  window.addEventListener('scroll', this.updateMobileMenuPositionWithThrottle);
124
124
  window.addEventListener('orientationchange', this.updateMobileMenuPositionWithThrottle);
125
+ window.addEventListener('popstate', this.popStateHandler);
125
126
  this.updateMobileMenuPosition();
126
127
  },
127
128
  beforeDestroy() {
128
129
  EventBus.$off('focus-on-default-product', this.focusOnDefaultProduct);
129
130
  window.removeEventListener('resize', this.updateMobileMenuPositionWithThrottle);
130
- window.addEventListener('scroll', this.updateMobileMenuPositionWithThrottle);
131
- window.addEventListener('orientationchange', this.updateMobileMenuPositionWithThrottle);
131
+ window.removeEventListener('scroll', this.updateMobileMenuPositionWithThrottle);
132
+ window.removeEventListener('orientationchange', this.updateMobileMenuPositionWithThrottle);
133
+ window.removeEventListener('popstate', this.popStateHandler);
132
134
  },
133
135
  methods: {
134
136
  ...mapMutations(['setEditModeSelectedLayer', 'removeTemplateLayer', 'setSelectedLayer']),
@@ -155,29 +157,60 @@ export default {
155
157
  focusOnDefaultProduct() {
156
158
  this.show('details');
157
159
  },
158
- showLayers() {
159
- this.setEditModeSelectedLayer(false);
160
- this.show('layers');
161
- },
162
- show(tab) {
160
+ show(tab, skipPushState) {
163
161
  if (tab === this.currentTab) {
164
162
  return;
165
163
  }
164
+
165
+ const prevTab = this.currentTab;
166
+
166
167
  if (this.isOpen) {
167
- this.hide();
168
+ this.hide(true);
168
169
  }
170
+
169
171
  this.$nextTick(() => {
170
172
  this.isOpen = true;
173
+
174
+ if(tab === 'layers') {
175
+ this.setEditModeSelectedLayer(false);
176
+ }
177
+
171
178
  this.currentTab = tab;
179
+
180
+ if (!skipPushState) {
181
+ this.pushState(this.currentTab);
182
+ }
172
183
  });
173
184
  },
174
- hide() {
185
+ hide(skipPushState) {
175
186
  if (this.selectedLayer?.type === 'text' && !this.selectedLayer.copy) {
176
187
  this.removeTemplateLayer(this.selectedLayer);
177
188
  }
189
+
178
190
  this.setSelectedLayer(null);
179
191
  this.isOpen = false;
192
+
180
193
  this.currentTab = null;
194
+
195
+ if (!skipPushState) {
196
+ this.pushState(this.currentTab);
197
+ }
198
+ },
199
+ pushState(tab) {
200
+ const state = { tab };
201
+ const title = '';
202
+ const url = `${window.location.href.replace(/#(.*)/, '')}#${tab || 'product'}`;
203
+ history.pushState(state, title, url);
204
+ console.log('state: ', state);
205
+ },
206
+ popStateHandler(event) {
207
+ const { tab } = event.state || {};
208
+ console.log('popStateHandler:tab: ', tab, event);
209
+ if (tab) {
210
+ this.show(tab, true);
211
+ } else {
212
+ this.hide(true);
213
+ }
181
214
  },
182
215
  async saveToCart() {
183
216
  if (this.addToCartDisabled) {
@@ -23,8 +23,8 @@
23
23
  bottom: -4px;
24
24
  left: -4px;
25
25
  border-radius: 13px;
26
- border: 2px solid $grey_1;
27
- transform: scale(0);
26
+ border: 2px solid $grey_4;
27
+ transform: scale(1);
28
28
  transition: transform .22s ease-in-out;
29
29
  }
30
30
  &-inner {
@@ -39,6 +39,7 @@
39
39
  &.selected {
40
40
  &:before {
41
41
  transform: scale(1);
42
+ border: 2px solid $grey_1;
42
43
  }
43
44
  }
44
45
  &-selected-icon {
@@ -3,6 +3,20 @@
3
3
  class="ProductColorImage"
4
4
  :class="{ zoomedIn }"
5
5
  :style="style">
6
+ <div
7
+ v-if="hasAdditionalColors"
8
+ :style="{
9
+ 'top': additionalColorHeight,
10
+ width: '100%',
11
+ height: '100%',
12
+ position: 'relative'
13
+ }">
14
+ <div
15
+ v-for="(additionalColorsThumb, index) of additionalColorsThumbs"
16
+ :key="index"
17
+ :style="additionalColorsThumb">
18
+ </div>
19
+ </div>
6
20
  </div>
7
21
  </template>
8
22
 
@@ -32,6 +46,23 @@ export default {
32
46
  },
33
47
  style() {
34
48
  return getColorBackgroundStyle(this.color);
49
+ },
50
+ hasAdditionalColors() {
51
+ return this.additionalColors.length > 0;
52
+ },
53
+ additionalColors() {
54
+ return (this.color.additionalColors || []);
55
+ },
56
+ additionalColorHeight() {
57
+ return `${100 / (this.additionalColors.length + 1)}%`;
58
+ },
59
+ additionalColorsThumbs() {
60
+ return (this.color.additionalColors || [])
61
+ .map(c => ({
62
+ ...getColorBackgroundStyle(c.color || c),
63
+ height: this.additionalColorHeight,
64
+ width: '100%'
65
+ }))
35
66
  }
36
67
  }
37
68
  };
@@ -72,6 +72,14 @@
72
72
  &__toggle-gst {
73
73
  margin-top: 20px;
74
74
  }
75
+ &__countries {
76
+ position: relative;
77
+ bottom: -13px;
78
+ display: flex;
79
+ justify-content: end;
80
+ font-size: 13px;
81
+ align-items: center;
82
+ }
75
83
  }
76
84
 
77
85
  ::v-deep .ProductColorsSelectorOptions {
@@ -73,6 +73,14 @@
73
73
  </checkbox>
74
74
  </div>
75
75
  </div>
76
+ <div class="ProductColorsSelector__countries">
77
+ <div>
78
+ Stock by countries:
79
+ </div>
80
+ <div>
81
+ <client-settings-stock-country />
82
+ </div>
83
+ </div>
76
84
  <products-size-selector-color
77
85
  :with-gst="inclGSTFinal"
78
86
  class="ProductColorsSelector__section"
@@ -92,6 +100,7 @@ import ProductColorsSelector from '@lancom/shared/components/product/layouts/pro
92
100
  import ProductColorsSelectorOptions from '@lancom/shared/components/product/layouts/product_colors_selector/product_colors_selector_options/product-colors-selector-options';
93
101
  import ProductColorImage from '@lancom/shared/components/product/product_color_image/product-color-image';
94
102
  import ProductsSizeSelectorColor from '@lancom/shared/components/product/products_size_selector_color/products-size-selector-color';
103
+ import ClientSettingsStockCountry from '@lancom/shared/components/common/client_settings_stock_country/client-settings-stock-country';
95
104
 
96
105
  export default {
97
106
  name: 'WorkdepotProductColorsSelector',
@@ -100,7 +109,8 @@ export default {
100
109
  ProductColorsSelectorOptions,
101
110
  ProductsSizeSelectorColor,
102
111
  ProductColorImage,
103
- Multiselect
112
+ Multiselect,
113
+ ClientSettingsStockCountry
104
114
  },
105
115
  data() {
106
116
  return {
@@ -14,6 +14,22 @@
14
14
  }
15
15
  }
16
16
  }
17
+ &__countries {
18
+ display: flex;
19
+ position: absolute;
20
+ top: 280px;
21
+ left: 0px;
22
+ right: 0px;
23
+ justify-content: center;
24
+ z-index: 999;
25
+ }
26
+ &__country {
27
+ width: 22px;
28
+ height: 16px;
29
+ background-repeat: no-repeat;
30
+ background-position: center center;
31
+ background-size: contain;
32
+ }
17
33
  &__tag {
18
34
  position: absolute;
19
35
  top: 14px;
@@ -94,6 +94,16 @@
94
94
  <div class="ProductListProduct__info-item mt-2 lc_caption">
95
95
  {{ product.colors.length }} {{ product.colors.length === 1 ? 'Colour' : 'Colours' }} | Size: {{ product.sizes | sizesRange }}
96
96
  </div>
97
+ <div class="ProductListProduct__countries">
98
+ <div
99
+ v-for="country in productCountries"
100
+ :key="country._id"
101
+ class="ProductListProduct__country"
102
+ :style="{
103
+ 'background-image': `url(${country.icon})`
104
+ }">
105
+ </div>
106
+ </div>
97
107
  <div
98
108
  v-if="full"
99
109
  class="ProductListProduct__info-item mt-2 lc_caption visible-full">
@@ -38,11 +38,13 @@
38
38
  width: 36px;
39
39
  height: 36px;
40
40
  border-radius: 13px;
41
+ overflow: hidden;
41
42
  flex-shrink: 0;
42
43
  &-image {
43
44
  width: 100%;
44
45
  height: 100%;
45
46
  border-radius: 11px;
47
+ overflow: hidden;
46
48
  }
47
49
  &--selected {
48
50
  border: 2px solid $grey_1;
@@ -2,7 +2,7 @@
2
2
  <a
3
3
  :href="link"
4
4
  class="ProductsLink"
5
- rel=”nofollow”>
5
+ rel="nofollow">
6
6
  <slot></slot>
7
7
  </a>
8
8
  </template>
@@ -222,7 +222,7 @@
222
222
  name: this.pageTitle,
223
223
  numberOfItems: this.count,
224
224
  itemListOrder: 'ItemListUnordered',
225
- itemListElement: this.products.map(product => {
225
+ itemListElement: this.products?.map(product => {
226
226
  const minPrintsPrice = product.minPrintsPrice || 0;
227
227
  const minProductPrice = product.isClearance ? product.minPriceWithoutClearance : product.minPrice;
228
228
  const minPrice = minProductPrice + minPrintsPrice;
@@ -259,7 +259,7 @@
259
259
  }
260
260
 
261
261
  return schema;
262
- })
262
+ }) || []
263
263
  };
264
264
 
265
265
  const breadcrumbSchema = {
@@ -1,3 +1,4 @@
1
+ import { mapGetters } from 'vuex';
1
2
  import { getColorBackgroundStyle, getProductMediumCover, getBgStyle, getProductHoverCover } from '@lancom/shared/assets/js/utils/colors';
2
3
  import { staticLink } from '@lancom/shared/assets/js/utils/filters';
3
4
  import { generateProductLink } from '@lancom/shared/assets/js/utils/product';
@@ -34,6 +35,10 @@ const productPreview = {
34
35
  };
35
36
  },
36
37
  computed: {
38
+ ...mapGetters(['countries']),
39
+ productCountries() {
40
+ return this.countries.filter(c => this.product.countries?.includes(c._id));
41
+ },
37
42
  noMinimum() {
38
43
  return !(this.product.minimumPrintOrderQuantity > 1) && !(this.product.minimumOrderQuantity > 1);
39
44
  },
@@ -20,10 +20,11 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
20
20
  async asyncData({ store, params, error, query, redirect }) {
21
21
  try {
22
22
  const { print, color, colour } = query;
23
- const { shop, country, currency } = store.getters;
23
+ const { shop, country, stockCountry, currency } = store.getters;
24
24
  const data = {
25
25
  shop: shop._id,
26
26
  slug: params.slug,
27
+ stockCountry: stockCountry?._id,
27
28
  country: country?._id,
28
29
  currency: currency?._id,
29
30
  print,
@@ -75,7 +76,7 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
75
76
  };
76
77
  },
77
78
  computed: {
78
- ...mapGetters(['shop', 'gstTax', 'country', 'currency']),
79
+ ...mapGetters(['shop', 'gstTax', 'country', 'stockCountry', 'currency']),
79
80
  ...mapGetters('product', ['product', 'simpleProducts', 'productDetails', 'editableColor', 'images', 'preSetPrints', 'editorSize', 'printsPrice']),
80
81
  pageItemImage() {
81
82
  return this.mainProductImageSrc;
@@ -115,6 +116,7 @@ export default (IS_PRODUCT_PRESET_PRINT_PRICING, isEditor = false) => ({
115
116
  shop: this.shop._id,
116
117
  slug,
117
118
  country: this.country?._id,
119
+ stockCountry: this.stockCountry?._id,
118
120
  currency: this.currency?._id,
119
121
  defaultColor: color || colour
120
122
  };
package/nuxt.config.js CHANGED
@@ -1,7 +1,7 @@
1
1
  const sharedRoutes = require('./routes');
2
2
  const feeds = require('./feeds');
3
3
 
4
- module.exports = (config, axios, { raygunClient, publicPath } = {}) => ({
4
+ module.exports = (config, axios, { raygunClient, publicPath, productUrlToEditor } = {}) => ({
5
5
  globalName: 'appLancom',
6
6
  mode: 'universal',
7
7
  head: {
@@ -38,7 +38,7 @@ module.exports = (config, axios, { raygunClient, publicPath } = {}) => ({
38
38
  '/customer/**'
39
39
  ],
40
40
  routes: async () => {
41
- const { data } = await axios.get(`${config.LOCAL_API_URL}/feed/sitemap?host=${config.HOST_NAME}`);
41
+ const { data } = await axios.get(`${config.LOCAL_API_URL}/feed/sitemap?host=${config.HOST_NAME}&toEditor=${!!productUrlToEditor}`);
42
42
  return [
43
43
  ...data.map(url => ({ url, priority: 0.8 })),
44
44
  // { url: `https://${config.HOST_NAME}`, priority: 1 }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lancom/shared",
3
- "version": "0.0.347",
3
+ "version": "0.0.349",
4
4
  "description": "lancom common scripts",
5
5
  "author": "e.tokovenko <e.tokovenko@gmail.com>",
6
6
  "repository": {
package/store/index.js CHANGED
@@ -7,6 +7,7 @@ const CLOSED_NOTIFICATION = 'lancom-closed-notification-1.0';
7
7
  import { getShopCountrySettings } from '@lancom/shared/assets/js/utils/shop';
8
8
 
9
9
  export const state = () => ({
10
+ stockCountry: null,
10
11
  country: null,
11
12
  currency: null,
12
13
  payment: null,
@@ -22,6 +23,7 @@ export const state = () => ({
22
23
  });
23
24
 
24
25
  export const getters = {
26
+ stockCountry: ({ stockCountry }) => stockCountry,
25
27
  country: ({ country }) => country,
26
28
  countries: ({ shop }) => (shop.countries || []).map(({ country }) => country).filter(c => !!c),
27
29
  currency: ({ currency }) => currency,
@@ -53,12 +55,14 @@ export const actions = {
53
55
  commit('setMenus', menus);
54
56
  commit('setShop', shop);
55
57
  // if (req.headers.cookie) {
56
- const { country, currency } = (req.headers.cookie && cookieparser.parse(req.headers.cookie)) || {};
58
+ const { country, currency, stockCountry } = (req.headers.cookie && cookieparser.parse(req.headers.cookie)) || {};
57
59
  const headers = (req && req.headers) ? Object.assign({}, req.headers) : {};
58
- const params = { country, currency, ip: headers['x-real-ip'] || req.ip };
60
+ const params = { country, currency, stockCountry, ip: headers['x-real-ip'] || req.ip };
59
61
  const settings = await api.fetchClientSettings(shop._id, params);
62
+ // console.log('settings: ', settings);
60
63
  commit('setCountry', settings.country);
61
64
  commit('setCurrency', settings.currency);
65
+ commit('setStockCountry', settings.stockCountry);
62
66
 
63
67
  const countrySetting = getShopCountrySettings(shop, settings.country);
64
68
  commit('setSettings', countrySetting);
@@ -137,6 +141,9 @@ export const mutations = {
137
141
  setCountry(state, country) {
138
142
  state.country = country;
139
143
  },
144
+ setStockCountry(state, stockCountry) {
145
+ state.stockCountry = stockCountry;
146
+ },
140
147
  setCurrency(state, currency) {
141
148
  state.currency = currency;
142
149
  },
package/store/product.js CHANGED
@@ -175,8 +175,8 @@ export const actions = {
175
175
  });
176
176
  }
177
177
  },
178
- async fetchProductDetails({ commit, state }, { shop, slug, country, currency, defaultColor = 'white' }) {
179
- const params = { country, currency };
178
+ async fetchProductDetails({ commit, state }, { shop, slug, country, stockCountry, currency, defaultColor = 'white' }) {
179
+ const params = { country, currency, stockCountry };
180
180
  const response = await api.fetchProductDetails(shop, slug, params);
181
181
  commit('setProductDetails', response);
182
182
 
package/store/products.js CHANGED
@@ -70,6 +70,7 @@ export const actions = {
70
70
  } catch (e) {
71
71
  const { status, data } = e?.response || {};
72
72
  const statusCode = status || 500;
73
+ // console.log('statusCode: ', statusCode, data?.error || 'Post not found');
73
74
  commit('setLoadError', {
74
75
  statusCode,
75
76
  error: data?.error || 'Post not found'