@ecomplus/storefront-components 1.0.0-beta.18 → 1.0.0-beta.181

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 (125) hide show
  1. package/CHANGELOG.md +1278 -133
  2. package/README.md +9 -4
  3. package/all.js +3 -1
  4. package/dist/1.storefront-components.min.js +2 -0
  5. package/dist/1.storefront-components.min.js.map +1 -0
  6. package/dist/2.storefront-components.min.js +5 -0
  7. package/dist/2.storefront-components.min.js.map +1 -0
  8. package/dist/3.storefront-components.min.js +5 -0
  9. package/dist/3.storefront-components.min.js.map +1 -0
  10. package/dist/storefront-components.min.js +33 -12
  11. package/dist/storefront-components.min.js.map +1 -1
  12. package/package.json +17 -12
  13. package/src/APagination.vue +2 -0
  14. package/src/AShare.vue +2 -0
  15. package/src/AccountAddresses.vue +3 -0
  16. package/src/AccountForm.vue +3 -0
  17. package/src/AccountPoints.vue +3 -0
  18. package/src/BuyTogether.vue +3 -0
  19. package/src/EarnPointsProgress.vue +3 -0
  20. package/src/ItemCustomizations.vue +2 -0
  21. package/src/KitProductVariations.vue +3 -0
  22. package/src/PointsApplier.vue +2 -0
  23. package/src/ProductQuickview.vue +3 -0
  24. package/src/QuantitySelector.vue +3 -0
  25. package/src/RecommendedItems.vue +3 -0
  26. package/src/ShippingLine.vue +1 -0
  27. package/src/TheCart.vue +3 -0
  28. package/src/html/APagination.html +90 -0
  29. package/src/html/APrices.html +24 -4
  30. package/src/html/AShare.html +31 -0
  31. package/src/html/AccountAddresses.html +90 -0
  32. package/src/html/AccountForm.html +269 -0
  33. package/src/html/AccountPoints.html +39 -0
  34. package/src/html/AddressForm.html +9 -7
  35. package/src/html/BuyTogether.html +60 -0
  36. package/src/html/CartItem.html +86 -38
  37. package/src/html/CartQuickview.html +28 -5
  38. package/src/html/DiscountApplier.html +2 -2
  39. package/src/html/EarnPointsProgress.html +28 -0
  40. package/src/html/InputDate.html +1 -1
  41. package/src/html/InputDocNumber.html +1 -0
  42. package/src/html/InputPhone.html +1 -1
  43. package/src/html/InstantSearch.html +3 -3
  44. package/src/html/ItemCustomizations.html +14 -0
  45. package/src/html/KitProductVariations.html +92 -0
  46. package/src/html/LoginBlock.html +34 -32
  47. package/src/html/LoginModal.html +9 -4
  48. package/src/html/PaymentOption.html +7 -5
  49. package/src/html/PointsApplier.html +26 -0
  50. package/src/html/ProductCard.html +56 -8
  51. package/src/html/ProductGallery.html +21 -3
  52. package/src/html/ProductQuickview.html +64 -0
  53. package/src/html/ProductVariations.html +30 -3
  54. package/src/html/QuantitySelector.html +85 -0
  55. package/src/html/RecommendedItems.html +48 -0
  56. package/src/html/SearchEngine.html +100 -24
  57. package/src/html/ShippingCalculator.html +84 -3
  58. package/src/html/ShippingLine.html +5 -2
  59. package/src/html/TheAccount.html +43 -9
  60. package/src/html/TheCart.html +156 -0
  61. package/src/html/TheProduct.html +416 -138
  62. package/src/js/APagination.js +74 -0
  63. package/src/js/APicture.js +27 -7
  64. package/src/js/APrices.js +80 -41
  65. package/src/js/AShare.js +83 -0
  66. package/src/js/AccountAddresses.js +192 -0
  67. package/src/js/AccountForm.js +312 -0
  68. package/src/js/AccountPoints.js +63 -0
  69. package/src/js/AddressForm.js +80 -35
  70. package/src/js/BuyTogether.js +246 -0
  71. package/src/js/CartItem.js +67 -14
  72. package/src/js/CartQuickview.js +20 -1
  73. package/src/js/DiscountApplier.js +165 -48
  74. package/src/js/EarnPointsProgress.js +77 -0
  75. package/src/js/InputDate.js +8 -8
  76. package/src/js/InputDocNumber.js +20 -0
  77. package/src/js/ItemCustomizations.js +10 -0
  78. package/src/js/KitProductVariations.js +218 -0
  79. package/src/js/LoginBlock.js +46 -5
  80. package/src/js/LoginModal.js +17 -4
  81. package/src/js/PaymentOption.js +28 -1
  82. package/src/js/PointsApplier.js +110 -0
  83. package/src/js/ProductCard.js +97 -11
  84. package/src/js/ProductGallery.js +32 -12
  85. package/src/js/ProductQuickview.js +72 -0
  86. package/src/js/ProductVariations.js +76 -19
  87. package/src/js/QuantitySelector.js +175 -0
  88. package/src/js/RecommendedItems.js +178 -0
  89. package/src/js/SearchEngine.js +185 -55
  90. package/src/js/ShippingCalculator.js +176 -35
  91. package/src/js/ShippingLine.js +35 -5
  92. package/src/js/TheAccount.js +97 -6
  93. package/src/js/TheCart.js +146 -0
  94. package/src/js/TheProduct.js +387 -43
  95. package/src/js/helpers/add-idle-callback.js +7 -0
  96. package/src/js/helpers/check-form-validity.js +3 -0
  97. package/src/js/helpers/favorite-products.js +24 -0
  98. package/src/js/helpers/scroll-to-element.js +10 -0
  99. package/src/js/helpers/sort-apps.js +14 -0
  100. package/src/js/helpers/wait-storefront-info.js +21 -0
  101. package/src/scss/APicture.scss +2 -0
  102. package/src/scss/APrices.scss +13 -1
  103. package/src/scss/AccountAddresses.scss +27 -0
  104. package/src/scss/AccountForm.scss +5 -0
  105. package/src/scss/AccountPoints.scss +17 -0
  106. package/src/scss/BuyTogether.scss +38 -0
  107. package/src/scss/CartItem.scss +17 -1
  108. package/src/scss/EarnPointsProgress.scss +6 -0
  109. package/src/scss/InstantSearch.scss +1 -0
  110. package/src/scss/KitProductVariations.scss +72 -0
  111. package/src/scss/LoginBlock.scss +5 -0
  112. package/src/scss/PaymentOption.scss +10 -1
  113. package/src/scss/ProductCard.scss +66 -28
  114. package/src/scss/ProductGallery.scss +4 -2
  115. package/src/scss/ProductQuickview.scss +36 -0
  116. package/src/scss/ProductVariations.scss +20 -4
  117. package/src/scss/QuantitySelector.scss +39 -0
  118. package/src/scss/RecommendedItems.scss +28 -0
  119. package/src/scss/SearchEngine.scss +9 -5
  120. package/src/scss/ShippingCalculator.scss +42 -1
  121. package/src/scss/ShippingLine.scss +24 -0
  122. package/src/scss/TheAccount.scss +4 -0
  123. package/src/scss/TheCart.scss +54 -0
  124. package/src/scss/TheProduct.scss +146 -1
  125. package/webpack.config.js +20 -6
@@ -33,6 +33,10 @@ export default {
33
33
  type: Boolean,
34
34
  default: true
35
35
  },
36
+ canFetchOauth: {
37
+ type: Boolean,
38
+ default: true
39
+ },
36
40
  ecomPassport: {
37
41
  type: Object,
38
42
  default () {
@@ -74,7 +78,9 @@ export default {
74
78
  getCustomer().main_email === this.email
75
79
  if (isIdentified) {
76
80
  this.$nextTick(() => {
77
- this.$refs.InputDoc.$el.focus()
81
+ if (this.$refs.InputDoc) {
82
+ this.$refs.InputDoc.$el.focus()
83
+ }
78
84
  })
79
85
  }
80
86
  return isIdentified
@@ -84,7 +90,8 @@ export default {
84
90
  if (!this.isWaitingLogin) {
85
91
  this.isWaitingLogin = true
86
92
  this.failAlertText = null
87
- const { email, docNumber } = this
93
+ const { docNumber } = this
94
+ const email = this.email && this.email.toLowerCase()
88
95
  const isAccountConfirm = this.confirmAccount()
89
96
  const emitUpdate = () => this.$emit('update', { email, docNumber })
90
97
  this.ecomPassport.fetchLogin(email, isAccountConfirm ? docNumber : null)
@@ -111,8 +118,12 @@ export default {
111
118
  }
112
119
  },
113
120
 
114
- oauthPopup (link) {
115
- this.ecomPassport.popupOauthLink(link)
121
+ oauthPopup (fnOrLink) {
122
+ if (typeof fnOrLink === 'function') {
123
+ fnOrLink()
124
+ } else {
125
+ this.ecomPassport.popupOauthLink(fnOrLink)
126
+ }
116
127
  this.isWaitingPopup = true
117
128
  setTimeout(() => {
118
129
  this.isWaitingPopup = false
@@ -131,14 +142,41 @@ export default {
131
142
  },
132
143
 
133
144
  created () {
145
+ if (Array.isArray(window.OAUTH_PROVIDERS)) {
146
+ this.oauthProviders = []
147
+ if (window.OAUTH_PROVIDERS.includes('google') && window.signInWithGoogle) {
148
+ this.oauthProviders.push({
149
+ link: window.signInWithGoogle,
150
+ faIcon: 'fa-google',
151
+ provider: 'google',
152
+ providerName: 'Google'
153
+ })
154
+ }
155
+ if (window.OAUTH_PROVIDERS.includes('facebook') && window.signInWithFacebook) {
156
+ this.oauthProviders.push({
157
+ link: window.signInWithFacebook,
158
+ faIcon: 'fa-facebook-f',
159
+ provider: 'facebook',
160
+ providerName: 'Facebook'
161
+ })
162
+ }
163
+ if (this.oauthProviders.length) return
164
+ }
165
+ if (!this.canFetchOauth) return
134
166
  this.ecomPassport.fetchOauthProviders()
135
167
  .then(({ host, baseUri, oauthPath, providers }) => {
136
168
  const oauthProviders = []
137
169
  for (const provider in providers) {
138
170
  if (providers[provider]) {
139
171
  const { faIcon, providerName } = providers[provider]
172
+ let link = host + baseUri + provider + oauthPath
173
+ const referral = typeof window === 'object' &&
174
+ window.sessionStorage.getItem('ecomReferral')
175
+ if (referral) {
176
+ link += `?referral=${referral}`
177
+ }
140
178
  oauthProviders.push({
141
- link: host + baseUri + provider + oauthPath,
179
+ link,
142
180
  faIcon,
143
181
  provider,
144
182
  providerName
@@ -162,6 +200,9 @@ export default {
162
200
  const customer = getCustomer()
163
201
  this.email = customer.main_email
164
202
  this.isCompany = customer.registry_type === 'j'
203
+ if (customer._id && customer.doc_number) {
204
+ this.$emit('login', this.ecomPassport)
205
+ }
165
206
  }
166
207
  }
167
208
  ecomPassport.on('login', () => {
@@ -8,6 +8,7 @@ import {
8
8
  i19login,
9
9
  i19logout,
10
10
  i19myAccount,
11
+ i19myFavorites,
11
12
  i19myOrders,
12
13
  i19noProfileFoundWithEmail,
13
14
  i19signInWith,
@@ -45,6 +46,10 @@ export default {
45
46
  type: String,
46
47
  default: '/app/#/account/orders'
47
48
  },
49
+ favoritesUrl: {
50
+ type: String,
51
+ default: '/app/#/account/favorites'
52
+ },
48
53
  ecomPassport: {
49
54
  type: Object,
50
55
  default () {
@@ -63,19 +68,21 @@ export default {
63
68
  oauthProviders: [],
64
69
  isLoginForm: false,
65
70
  hasLoginError: false,
66
- hasNoProfileFound: false
71
+ hasNoProfileFound: false,
72
+ isWrongCode: false
67
73
  }
68
74
  },
69
75
 
70
76
  computed: {
71
77
  i19close: () => i18n(i19close),
72
78
  i19continueLoginOnPopup: () => i18n(i19continueLoginOnPopup),
73
- i19email: () => i18n(i19email),
79
+ i19email: () => i18n(i19email).toLowerCase(),
74
80
  i19guestCheckoutMsg: () => i18n(i19guestCheckoutMsg),
75
81
  i19loginErrorMsg: () => i18n(i19loginErrorMsg),
76
82
  i19login: () => i18n(i19login),
77
83
  i19logout: () => i18n(i19logout),
78
84
  i19myAccount: () => i18n(i19myAccount),
85
+ i19myFavorites: () => i18n(i19myFavorites),
79
86
  i19myOrders: () => i18n(i19myOrders),
80
87
  i19noProfileFoundWithEmail: () => i18n(i19noProfileFoundWithEmail),
81
88
  i19signInWith: () => i18n(i19signInWith),
@@ -120,8 +127,14 @@ export default {
120
127
  for (const provider in providers) {
121
128
  if (providers[provider]) {
122
129
  const { faIcon, providerName } = providers[provider]
130
+ let link = host + baseUri + provider + oauthPath
131
+ const referral = typeof window === 'object' &&
132
+ window.sessionStorage.getItem('ecomReferral')
133
+ if (referral) {
134
+ link += `?referral=${referral}`
135
+ }
123
136
  oauthProviders.push({
124
- link: host + baseUri + provider + oauthPath,
137
+ link,
125
138
  faIcon,
126
139
  provider,
127
140
  providerName
@@ -180,7 +193,7 @@ export default {
180
193
 
181
194
  submitEmail () {
182
195
  this.isLoginForm = false
183
- const promise = this.ecomPassport.fetchLogin(this.email)
196
+ const promise = this.ecomPassport.fetchLogin(this.email.toLowerCase())
184
197
  .catch(err => {
185
198
  const { response } = err
186
199
  if (response && response.status === 403) {
@@ -19,6 +19,7 @@ export default {
19
19
  type: Object,
20
20
  required: true
21
21
  },
22
+ installmentsOption: Object,
22
23
  price: Number
23
24
  },
24
25
 
@@ -43,6 +44,31 @@ export default {
43
44
  return this.paymentGateway.installment_options.concat().sort((a, b) => {
44
45
  return a.number - b.number
45
46
  })
47
+ } else if (
48
+ this.price &&
49
+ this.installmentsOption &&
50
+ this.paymentGateway.payment_method.code === 'credit_card'
51
+ ) {
52
+ const installmentOptions = []
53
+ for (let number = 2; number <= this.installmentsOption.max_number; number++) {
54
+ const tax = this.installmentsOption.monthly_interest > 0
55
+ const minInstallment = this.installmentsOption.min_installment || 5
56
+ let value
57
+ if (!tax) {
58
+ value = this.price / number
59
+ } else {
60
+ const interest = this.installmentsOption.monthly_interest / 100
61
+ value = this.price * interest / (1 - Math.pow(1 + interest, -number))
62
+ }
63
+ if (value >= minInstallment) {
64
+ installmentOptions.push({
65
+ number,
66
+ value,
67
+ tax
68
+ })
69
+ }
70
+ }
71
+ return installmentOptions
46
72
  }
47
73
  },
48
74
 
@@ -51,7 +77,8 @@ export default {
51
77
  if (this.discount.type === 'percentage') {
52
78
  return this.price * (100 - this.discount.value) / 100
53
79
  } else {
54
- return Math.min(this.price - this.discount.value, 0)
80
+ const finalPrice = this.price - this.discount.value
81
+ return (finalPrice > 0 ? finalPrice : 0)
55
82
  }
56
83
  }
57
84
  return this.price
@@ -0,0 +1,110 @@
1
+ import { i19use$1LoyaltyPoints } from '@ecomplus/i18n'
2
+
3
+ import {
4
+ i18n,
5
+ formatMoney
6
+ } from '@ecomplus/utils'
7
+
8
+ import ecomPassport from '@ecomplus/passport-client'
9
+ import waitStorefrontInfo from './helpers/wait-storefront-info'
10
+
11
+ export default {
12
+ name: 'PointsApplier',
13
+
14
+ props: {
15
+ pointsPrograms: Object,
16
+ pointsApplied: {
17
+ type: Object,
18
+ required: true
19
+ },
20
+ pointsAmount: Number,
21
+ maxPointsAmount: Number,
22
+ ecomPassport: {
23
+ type: Object,
24
+ default () {
25
+ return ecomPassport
26
+ }
27
+ }
28
+ },
29
+
30
+ data () {
31
+ return {
32
+ localPointsPrograms: this.pointsPrograms || {},
33
+ availablePoints: {}
34
+ }
35
+ },
36
+
37
+ computed: {
38
+ i19use$1LoyaltyPoints: () => i18n(i19use$1LoyaltyPoints)
39
+ },
40
+
41
+ methods: {
42
+ formatMoney,
43
+
44
+ updatePrograms (pointsPrograms) {
45
+ if (pointsPrograms && Object.keys(pointsPrograms).length) {
46
+ this.localPointsPrograms = pointsPrograms
47
+ this.$emit('update:points-programs', pointsPrograms)
48
+ this.$nextTick(this.fixAvailablePoints)
49
+ }
50
+ },
51
+
52
+ fixAvailablePoints () {
53
+ const pointsEntries = this.ecomPassport.getCustomer().loyalty_points_entries
54
+ this.availablePoints = pointsEntries
55
+ ? pointsEntries.reduce((availablePoints, pointsEntry) => {
56
+ const validThru = pointsEntry.valid_thru
57
+ if (!validThru || new Date(validThru).getTime() >= Date.now()) {
58
+ const programId = pointsEntry.program_id
59
+ const points = pointsEntry.active_points
60
+ if (this.localPointsPrograms[programId]) {
61
+ if (availablePoints[programId]) {
62
+ availablePoints[programId] += points
63
+ } else {
64
+ availablePoints[programId] = points
65
+ }
66
+ if (availablePoints[programId] > pointsEntry.max_points) {
67
+ availablePoints[programId] = pointsEntry.max_points
68
+ }
69
+ const { ratio } = this.localPointsPrograms[programId]
70
+ if (availablePoints[programId] * ratio > this.maxPointsAmount) {
71
+ availablePoints[programId] = this.maxPointsAmount / ratio
72
+ }
73
+ }
74
+ }
75
+ return availablePoints
76
+ }, {})
77
+ : {}
78
+ },
79
+
80
+ getPointsAmount (programId) {
81
+ return this.availablePoints[programId] * this.localPointsPrograms[programId].ratio
82
+ },
83
+
84
+ togglePoints (programId, isChecked) {
85
+ const pointsApplied = {}
86
+ if (isChecked) {
87
+ pointsApplied[programId] = this.availablePoints[programId]
88
+ }
89
+ this.$emit('update:points-applied', pointsApplied)
90
+ let pointsAmount = 0
91
+ for (programId in pointsApplied) {
92
+ pointsAmount += this.getPointsAmount(programId)
93
+ }
94
+ this.$emit('update:points-amount', pointsAmount)
95
+ }
96
+ },
97
+
98
+ created () {
99
+ if (!this.pointsPrograms || !Object.keys(this.pointsPrograms).length) {
100
+ waitStorefrontInfo('list_payments', 'loyalty_points_programs')
101
+ .then(this.updatePrograms)
102
+ } else {
103
+ this.fixAvailablePoints()
104
+ }
105
+ this.ecomPassport.on('login', this.fixAvailablePoints)
106
+ this.$once('hook:beforeDestroy', () => {
107
+ this.ecomPassport.off('login', this.fixAvailablePoints)
108
+ })
109
+ }
110
+ }
@@ -1,4 +1,5 @@
1
1
  import {
2
+ i19addToFavorites,
2
3
  i19buy,
3
4
  i19connectionErrorProductMsg,
4
5
  i19outOfStock,
@@ -13,11 +14,27 @@ import {
13
14
  price as getPrice
14
15
  } from '@ecomplus/utils'
15
16
 
17
+ import Vue from 'vue'
16
18
  import { store } from '@ecomplus/client'
17
19
  import ecomCart from '@ecomplus/shopping-cart'
18
20
  import ALink from '../ALink.vue'
19
21
  import APicture from '../APicture.vue'
20
22
  import APrices from '../APrices.vue'
23
+ import ecomPassport from '@ecomplus/passport-client'
24
+ import { toggleFavorite, checkFavorite } from './helpers/favorite-products'
25
+
26
+ const getExternalHtml = (varName, product) => {
27
+ if (typeof window === 'object') {
28
+ varName = `productCard${varName}Html`
29
+ const html = typeof window[varName] === 'function'
30
+ ? window[varName](product)
31
+ : window[varName]
32
+ if (typeof html === 'string') {
33
+ return html
34
+ }
35
+ }
36
+ return undefined
37
+ }
21
38
 
22
39
  export default {
23
40
  name: 'ProductCard',
@@ -37,27 +54,61 @@ export default {
37
54
  default: 'h3'
38
55
  },
39
56
  buyText: String,
57
+ transitionClass: {
58
+ type: String,
59
+ default: 'animated fadeIn'
60
+ },
40
61
  canAddToCart: {
41
62
  type: Boolean,
42
63
  default: true
43
64
  },
44
- isLoaded: Boolean
65
+ ecomPassport: {
66
+ type: Object,
67
+ default () {
68
+ return ecomPassport
69
+ }
70
+ },
71
+ accountUrl: {
72
+ type: String,
73
+ default: '/app/#/account/'
74
+ },
75
+ isLoaded: Boolean,
76
+ installmentsOption: Object,
77
+ discountOption: Object
45
78
  },
46
79
 
47
80
  data () {
48
81
  return {
49
82
  body: {},
50
83
  isLoading: false,
84
+ isWaitingBuy: false,
51
85
  isHovered: false,
86
+ isFavorite: false,
52
87
  error: ''
53
88
  }
54
89
  },
55
90
 
56
91
  computed: {
92
+ i19addToFavorites: () => i18n(i19addToFavorites),
57
93
  i19outOfStock: () => i18n(i19outOfStock),
58
94
  i19unavailable: () => i18n(i19unavailable),
59
- buyHtml: () => typeof window === 'object' && window.productCardBuyHtml,
60
- footerHtml: () => typeof window === 'object' && window.productCardFooterHtml,
95
+ i19uponRequest: () => 'Sob consulta',
96
+
97
+ isWithoutPrice () {
98
+ return !getPrice(this.body)
99
+ },
100
+
101
+ ratingHtml () {
102
+ return getExternalHtml('Rating', this.body)
103
+ },
104
+
105
+ buyHtml () {
106
+ return getExternalHtml('Buy', this.body)
107
+ },
108
+
109
+ footerHtml () {
110
+ return getExternalHtml('Footer', this.body)
111
+ },
61
112
 
62
113
  name () {
63
114
  return getName(this.body)
@@ -77,6 +128,10 @@ export default {
77
128
  return this.body.available && this.body.visible && this.isInStock
78
129
  },
79
130
 
131
+ isLogged () {
132
+ return ecomPassport.checkAuthorization()
133
+ },
134
+
80
135
  discount () {
81
136
  const { body } = this
82
137
  return checkOnPromotion(body)
@@ -90,13 +145,15 @@ export default {
90
145
  this.body = Object.assign({}, data)
91
146
  delete this.body.body_html
92
147
  delete this.body.body_text
148
+ delete this.body.inventory_records
149
+ delete this.body.price_change_records
150
+ this.isFavorite = checkFavorite(this.body._id, this.ecomPassport)
93
151
  },
94
152
 
95
153
  fetchItem () {
96
154
  if (this.productId) {
97
155
  this.isLoading = true
98
- const { storeId, productId } = this
99
- store({ url: `/products/${productId}.json`, storeId })
156
+ store({ url: `/products/${this.productId}.json` })
100
157
  .then(({ data }) => {
101
158
  this.$emit('update:product', data)
102
159
  this.setBody(data)
@@ -114,16 +171,45 @@ export default {
114
171
  }
115
172
  },
116
173
 
174
+ toggleFavorite () {
175
+ if (this.isLogged) {
176
+ this.isFavorite = toggleFavorite(this.body._id, this.ecomPassport)
177
+ }
178
+ },
179
+
117
180
  buy () {
118
181
  const product = this.body
119
182
  this.$emit('buy', { product })
120
183
  if (this.canAddToCart) {
121
- const { variations, slug } = product
122
- if (variations && variations.length) {
123
- window.location = `/${slug}`
124
- } else {
125
- ecomCart.addProduct(product)
126
- }
184
+ this.isWaitingBuy = true
185
+ store({ url: `/products/${product._id}.json` })
186
+ .then(({ data }) => {
187
+ const selectFields = ['variations', 'customizations', 'kit_composition']
188
+ for (let i = 0; i < selectFields.length; i++) {
189
+ const selectOptions = data[selectFields[i]]
190
+ if (selectOptions && selectOptions.length) {
191
+ return import('../ProductQuickview.vue')
192
+ .then(quickview => {
193
+ new Vue({
194
+ render: h => h(quickview.default, {
195
+ props: {
196
+ product: data
197
+ }
198
+ })
199
+ }).$mount(this.$refs.quickview)
200
+ })
201
+ }
202
+ }
203
+ const { quantity, price } = data
204
+ ecomCart.addProduct({ ...product, quantity, price })
205
+ })
206
+ .catch(err => {
207
+ console.error(err)
208
+ window.location = `/${product.slug}`
209
+ })
210
+ .finally(() => {
211
+ this.isWaitingBuy = false
212
+ })
127
213
  }
128
214
  }
129
215
  },
@@ -18,8 +18,6 @@ import {
18
18
 
19
19
  import ecomCart from '@ecomplus/shopping-cart'
20
20
  import Glide from '@glidejs/glide'
21
- import * as PhotoSwipe from 'photoswipe'
22
- import * as psUi from 'photoswipe/dist/photoswipe-ui-default'
23
21
  import APicture from '../APicture.vue'
24
22
 
25
23
  export default {
@@ -43,7 +41,7 @@ export default {
43
41
  video: Object,
44
42
  videoAspectRatio: {
45
43
  type: String,
46
- default: '4by3'
44
+ default: '16by9'
47
45
  },
48
46
  canAddToCart: {
49
47
  type: Boolean,
@@ -62,7 +60,8 @@ export default {
62
60
  rewind: false
63
61
  }
64
62
  }
65
- }
63
+ },
64
+ isSSR: Boolean
66
65
  },
67
66
 
68
67
  data () {
@@ -70,7 +69,9 @@ export default {
70
69
  glide: null,
71
70
  pswp: null,
72
71
  activeIndex: null,
73
- isSliderMoved: false
72
+ isSliderMoved: false,
73
+ elFirstPicture: null,
74
+ zoomLinkStyle: null
74
75
  }
75
76
  },
76
77
 
@@ -86,7 +87,8 @@ export default {
86
87
 
87
88
  localPictures () {
88
89
  return this.pictures && this.pictures.length
89
- ? this.pictures : (this.product.pictures || [])
90
+ ? this.pictures
91
+ : (this.product.pictures || [])
90
92
  },
91
93
 
92
94
  videoSrc () {
@@ -162,13 +164,23 @@ export default {
162
164
  },
163
165
 
164
166
  openZoom (index) {
165
- if (!this.pswd) {
166
- this.pswp = new PhotoSwipe(this.$refs.pswp, psUi, this.pswpItems, {
167
- ...this.pswpOptions,
168
- index
167
+ this.zoomLinkStyle = 'cursor: wait'
168
+ return import(/* webpackPrefetch: true */ 'photoswipe')
169
+ .then(pack => {
170
+ const PhotoSwipe = pack.default
171
+ return import(/* webpackPrefetch: true */ 'photoswipe/dist/photoswipe-ui-default').then(pack => {
172
+ const psUi = pack.default
173
+ this.pswp = new PhotoSwipe(this.$refs.pswp, psUi, this.pswpItems, {
174
+ ...this.pswpOptions,
175
+ index
176
+ })
177
+ this.pswp.init()
178
+ })
179
+ })
180
+ .catch(console.error)
181
+ .finally(() => {
182
+ this.zoomLinkStyle = null
169
183
  })
170
- }
171
- this.pswp.init()
172
184
  },
173
185
 
174
186
  buy () {
@@ -203,6 +215,14 @@ export default {
203
215
  },
204
216
 
205
217
  mounted () {
218
+ if (this.isSSR) {
219
+ this.elFirstPicture = document.querySelector('#product-gallery .product__picture')
220
+ if (this.elFirstPicture) {
221
+ this.$nextTick(() => {
222
+ this.$refs.firstPicture[0].appendChild(this.elFirstPicture)
223
+ })
224
+ }
225
+ }
206
226
  const glide = new Glide(this.$refs.glide, this.glideOptions)
207
227
  glide.on('run', () => {
208
228
  this.moveSlider(glide.index)
@@ -0,0 +1,72 @@
1
+ import {
2
+ i19close,
3
+ i19seeMoreInfo
4
+ } from '@ecomplus/i18n'
5
+
6
+ import { i18n } from '@ecomplus/utils'
7
+ import { Portal } from '@linusborg/vue-simple-portal'
8
+ import TheProduct from '../TheProduct.vue'
9
+ import ABackdrop from '../ABackdrop.vue'
10
+
11
+ export default {
12
+ name: 'ProductQuickView',
13
+
14
+ components: {
15
+ Portal,
16
+ TheProduct,
17
+ ABackdrop
18
+ },
19
+
20
+ props: {
21
+ productId: String,
22
+ product: Object,
23
+ portalId: {
24
+ type: String,
25
+ default: 'quickview'
26
+ }
27
+ },
28
+
29
+ data () {
30
+ return {
31
+ productName: '',
32
+ productLink: '',
33
+ isVisible: false
34
+ }
35
+ },
36
+
37
+ computed: {
38
+ i19close: () => i18n(i19close),
39
+ i19seeMoreInfo: () => i18n(i19seeMoreInfo)
40
+ },
41
+
42
+ methods: {
43
+ setProduct ({ name, slug }) {
44
+ this.productName = name
45
+ this.productLink = `/${slug}`
46
+ },
47
+
48
+ hide () {
49
+ this.isVisible = false
50
+ setTimeout(() => {
51
+ if (!this.isVisible) {
52
+ this.$destroy()
53
+ }
54
+ }, 450)
55
+ }
56
+ },
57
+
58
+ created () {
59
+ let portal = document.getElementById(this.portalId)
60
+ if (!portal) {
61
+ portal = document.createElement('div')
62
+ portal.setAttribute('id', this.portalId)
63
+ document.body.appendChild(portal)
64
+ } else {
65
+ portal.innerHTML = ''
66
+ }
67
+ if (this.product) {
68
+ this.setProduct(this.product)
69
+ }
70
+ this.isVisible = true
71
+ }
72
+ }