@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
@@ -1,5 +1,7 @@
1
1
  import {
2
+ i19add$1ToEarn,
2
3
  i19calculateShipping,
4
+ i19freeShipping,
3
5
  i19zipCode
4
6
  } from '@ecomplus/i18n'
5
7
 
@@ -11,11 +13,13 @@ import {
11
13
  } from '@ecomplus/utils'
12
14
 
13
15
  import { modules } from '@ecomplus/client'
16
+ import sortApps from './helpers/sort-apps'
14
17
  import CleaveInput from 'vue-cleave-component'
15
18
  import ShippingLine from '../ShippingLine.vue'
16
19
 
17
20
  const localStorage = typeof window === 'object' && window.localStorage
18
21
  const zipStorageKey = 'shipping-to-zip'
22
+ const globalOpts = (typeof window === 'object' && window.propsShippingCalculator) || {}
19
23
 
20
24
  const reduceItemBody = itemOrProduct => {
21
25
  const shippedItem = {}
@@ -25,6 +29,7 @@ const reduceItemBody = itemOrProduct => {
25
29
  'sku',
26
30
  'name',
27
31
  'quantity',
32
+ 'inventory',
28
33
  'currency_id',
29
34
  'currency_symbol',
30
35
  'price',
@@ -50,9 +55,17 @@ export default {
50
55
  props: {
51
56
  zipCode: String,
52
57
  canSelectServices: Boolean,
58
+ canAutoSelectService: {
59
+ type: Boolean,
60
+ default: typeof globalOpts.canAutoSelectService === 'boolean'
61
+ ? globalOpts.canAutoSelectService
62
+ : true
63
+ },
53
64
  canInputZip: {
54
65
  type: Boolean,
55
- default: true
66
+ default: typeof globalOpts.canInputZip === 'boolean'
67
+ ? globalOpts.canInputZip
68
+ : true
56
69
  },
57
70
  countryCode: {
58
71
  type: String,
@@ -75,26 +88,66 @@ export default {
75
88
  default () {
76
89
  return {}
77
90
  }
91
+ },
92
+ skipAppIds: Array,
93
+ shippingAppsSort: {
94
+ type: Array,
95
+ default () {
96
+ return window.ecomShippingApps || []
97
+ }
78
98
  }
79
99
  },
80
100
 
81
101
  data () {
82
102
  return {
83
103
  localZipCode: null,
104
+ localShippedItems: [],
105
+ amountSubtotal: null,
84
106
  shippingServices: [],
85
107
  selectedService: null,
86
- isWaiting: false
108
+ hasPaidOption: false,
109
+ freeFromValue: null,
110
+ isScheduled: false,
111
+ retryTimer: null,
112
+ isWaiting: false,
113
+ hasCalculated: false
87
114
  }
88
115
  },
89
116
 
90
117
  computed: {
118
+ i19add$1ToEarn: () => i18n(i19add$1ToEarn),
91
119
  i19calculateShipping: () => i18n(i19calculateShipping),
92
120
  i19zipCode: () => i18n(i19zipCode),
121
+ i19freeShipping: () => i18n(i19freeShipping).toLowerCase(),
122
+ i19selectShippingMsg: () => i18n({
123
+ pt_br: 'Selecione uma forma de envio abaixo',
124
+ en_us: 'Select a shipping method below'
125
+ }),
93
126
 
94
127
  cleaveOptions () {
95
128
  return this.countryCode === 'BR'
96
129
  ? { blocks: [5, 3], delimiter: '-' }
97
130
  : { blocks: [30] }
131
+ },
132
+
133
+ freeFromPercentage () {
134
+ return this.hasPaidOption && this.amountSubtotal < this.freeFromValue
135
+ ? Math.round(this.amountSubtotal * 100 / this.freeFromValue)
136
+ : null
137
+ },
138
+
139
+ productionDeadline () {
140
+ let maxDeadline = 0
141
+ this.shippedItems.forEach(item => {
142
+ if (item.quantity && item.production_time) {
143
+ const { days, cumulative } = item.production_time
144
+ const itemDeadline = cumulative ? days * item.quantity : days
145
+ if (itemDeadline > maxDeadline) {
146
+ maxDeadline = itemDeadline
147
+ }
148
+ }
149
+ })
150
+ return maxDeadline
98
151
  }
99
152
  },
100
153
 
@@ -105,56 +158,119 @@ export default {
105
158
  this.$emit('update:zip-code', this.localZipCode)
106
159
  },
107
160
 
108
- parseShippingOptions (shippingResult = [], isRetry) {
161
+ parseShippingOptions (shippingResult = [], isRetry = false) {
162
+ this.freeFromValue = null
109
163
  this.shippingServices = []
110
- let canRetry
111
164
  if (shippingResult.length) {
112
165
  shippingResult.forEach(appResult => {
113
166
  const { validated, error, response } = appResult
114
- if (validated && !error) {
115
- response.shipping_services.forEach(service => {
116
- this.shippingServices.push({
117
- app_id: appResult.app_id,
118
- ...service
119
- })
167
+ if (!validated || error) {
168
+ return
169
+ }
170
+ if (
171
+ this.skipAppIds &&
172
+ this.skipAppIds.includes(appResult.app_id) &&
173
+ shippingResult.filter(({ app_id: appId }) => !this.skipAppIds.includes(appId)).length
174
+ ) {
175
+ return
176
+ }
177
+ response.shipping_services.forEach(service => {
178
+ this.shippingServices.push({
179
+ app_id: appResult.app_id,
180
+ ...service
120
181
  })
121
- } else if (isRetry !== true && (!response || !response.error)) {
122
- canRetry = true
182
+ })
183
+ const freeShippingFromValue = response.free_shipping_from_value
184
+ if (
185
+ freeShippingFromValue &&
186
+ (!this.freeFromValue || this.freeFromValue > freeShippingFromValue)
187
+ ) {
188
+ this.freeFromValue = freeShippingFromValue
123
189
  }
124
190
  })
125
191
  if (!this.shippingServices.length) {
126
- if (canRetry) {
192
+ if (!isRetry) {
127
193
  this.fetchShippingServices(true)
194
+ } else {
195
+ this.scheduleRetry()
128
196
  }
129
197
  } else {
130
- this.setSelectedService(0)
198
+ this.shippingServices = this.shippingServices.sort((a, b) => {
199
+ const priceDiff = a.shipping_line.total_price - b.shipping_line.total_price
200
+ return priceDiff < 0
201
+ ? -1
202
+ : priceDiff > 0
203
+ ? 1
204
+ : a.shipping_line.delivery_time && b.shipping_line.delivery_time &&
205
+ a.shipping_line.delivery_time.days < b.shipping_line.delivery_time.days
206
+ ? -1
207
+ : 1
208
+ })
209
+ if (this.canAutoSelectService) {
210
+ this.setSelectedService(0)
211
+ } else {
212
+ this.selectedService = null
213
+ }
214
+ this.hasPaidOption = Boolean(this.shippingServices.find(service => {
215
+ return service.shipping_line.total_price || service.shipping_line.price
216
+ }))
217
+ if (Array.isArray(this.shippingAppsSort) && this.shippingAppsSort.length) {
218
+ this.shippingServices = sortApps(this.shippingServices, this.shippingAppsSort)
219
+ }
131
220
  }
132
221
  }
133
222
  },
134
223
 
135
- fetchShippingServices (isRetry) {
136
- const { storeId } = this
137
- const url = '/calculate_shipping.json'
138
- const method = 'POST'
139
- const data = {
140
- ...this.shippingData,
141
- to: {
142
- zip: this.localZipCode,
143
- ...this.shippingData.to
224
+ scheduleRetry (timeout = 10000) {
225
+ clearTimeout(this.retryTimer)
226
+ this.retryTimer = setTimeout(() => {
227
+ if (this.localZipCode && !this.shippingServices.length && this.shippedItems.length) {
228
+ this.fetchShippingServices(true)
144
229
  }
230
+ }, timeout)
231
+ },
232
+
233
+ fetchShippingServices (isRetry) {
234
+ if (!this.isScheduled) {
235
+ this.isScheduled = true
236
+ setTimeout(() => {
237
+ this.isScheduled = false
238
+ const { storeId } = this
239
+ let url = '/calculate_shipping.json'
240
+ if (this.skipAppIds && this.skipAppIds.length) {
241
+ url += '?skip_ids='
242
+ this.skipAppIds.forEach((appId, i) => {
243
+ if (i > 0) url += ','
244
+ url += `${appId}`
245
+ })
246
+ }
247
+ const method = 'POST'
248
+ const data = {
249
+ ...this.shippingData,
250
+ to: {
251
+ zip: this.localZipCode,
252
+ ...this.shippingData.to
253
+ }
254
+ }
255
+ if (this.localShippedItems.length) {
256
+ data.items = this.localShippedItems
257
+ data.subtotal = this.amountSubtotal
258
+ }
259
+ this.isWaiting = true
260
+ modules({ url, method, storeId, data })
261
+ .then(({ data }) => this.parseShippingOptions(data.result, isRetry))
262
+ .catch(err => {
263
+ if (!isRetry) {
264
+ this.scheduleRetry(4000)
265
+ }
266
+ console.error(err)
267
+ })
268
+ .finally(() => {
269
+ this.hasCalculated = true
270
+ this.isWaiting = false
271
+ })
272
+ }, this.hasCalculated ? 150 : 50)
145
273
  }
146
- if (this.shippedItems.length) {
147
- data.items = this.shippedItems.map(reduceItemBody)
148
- const itemsToSubtotal = (subtotal, item) => subtotal + getPrice(item) * item.quantity
149
- data.subtotal = data.items.reduce(itemsToSubtotal, 0)
150
- }
151
- this.isWaiting = true
152
- modules({ url, method, storeId, data })
153
- .then(({ data }) => this.parseShippingOptions(data.result, isRetry))
154
- .catch(console.error)
155
- .finally(() => {
156
- this.isWaiting = false
157
- })
158
274
  },
159
275
 
160
276
  submitZipCode () {
@@ -174,6 +290,27 @@ export default {
174
290
  },
175
291
 
176
292
  watch: {
293
+ shippedItems: {
294
+ handler () {
295
+ setTimeout(() => {
296
+ this.localShippedItems = this.shippedItems.map(reduceItemBody)
297
+ const { amountSubtotal } = this
298
+ this.amountSubtotal = this.shippedItems.reduce((subtotal, item) => {
299
+ return subtotal + getPrice(item) * item.quantity
300
+ }, 0)
301
+ if (
302
+ this.hasCalculated &&
303
+ (this.canSelectServices || amountSubtotal !== this.amountSubtotal ||
304
+ (!this.shippingServices.length && !this.isWaiting))
305
+ ) {
306
+ this.fetchShippingServices()
307
+ }
308
+ }, 50)
309
+ },
310
+ deep: true,
311
+ immediate: true
312
+ },
313
+
177
314
  localZipCode (zipCode) {
178
315
  if (this.countryCode === 'BR' && zipCode.replace(/\D/g, '').length === 8) {
179
316
  this.submitZipCode()
@@ -189,6 +326,10 @@ export default {
189
326
  immediate: true
190
327
  },
191
328
 
329
+ skipAppIds () {
330
+ this.fetchShippingServices()
331
+ },
332
+
192
333
  shippingResult: {
193
334
  handler (result) {
194
335
  if (result.length) {
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  i19days,
3
+ i19free,
3
4
  i19freeShipping,
5
+ i19pickUpToday,
6
+ i19receiveToday,
7
+ i19untilTomorrow,
4
8
  i19upTo,
5
9
  i19workingDays
6
10
  } from '@ecomplus/i18n'
@@ -10,6 +14,8 @@ import {
10
14
  formatMoney
11
15
  } from '@ecomplus/utils'
12
16
 
17
+ const globalOpts = (typeof window === 'object' && window.propsShippingLine) || {}
18
+
13
19
  export default {
14
20
  name: 'ShippingLine',
15
21
 
@@ -17,6 +23,14 @@ export default {
17
23
  shippingLine: {
18
24
  type: Object,
19
25
  required: true
26
+ },
27
+ productionDeadline: {
28
+ type: Number,
29
+ default: 0
30
+ },
31
+ getDeadlineStr: {
32
+ type: Function,
33
+ default: globalOpts.getDeadlineStr
20
34
  }
21
35
  },
22
36
 
@@ -29,17 +43,33 @@ export default {
29
43
  if (shipping.delivery_time) {
30
44
  days += shipping.delivery_time.days
31
45
  }
32
- return `${i18n(i19upTo)} ${days} ${i18n(isWorkingDays ? i19workingDays : i19days)}`
46
+ days += this.productionDeadline
47
+ if (this.getDeadlineStr) {
48
+ const str = this.getDeadlineStr({
49
+ days,
50
+ isWorkingDays,
51
+ shippingLine: this.shippingLine
52
+ })
53
+ if (str) return str
54
+ }
55
+ if (days > 1) {
56
+ return `${i18n(i19upTo)} ${days} ` +
57
+ i18n(isWorkingDays ? i19workingDays : i19days).toLowerCase()
58
+ }
59
+ return i18n(days === 1
60
+ ? i19untilTomorrow
61
+ : shipping.pick_up ? i19pickUpToday : i19receiveToday)
33
62
  },
34
63
 
35
64
  freightValueStr () {
36
- const freight = typeof this.shippingLine.total_price === 'number'
37
- ? this.shippingLine.total_price
38
- : this.shippingLine.price
65
+ const { shippingLine } = this
66
+ const freight = typeof shippingLine.total_price === 'number'
67
+ ? shippingLine.total_price
68
+ : shippingLine.price
39
69
  if (freight) {
40
70
  return formatMoney(freight)
41
71
  } else {
42
- return i18n(i19freeShipping)
72
+ return i18n(shippingLine.pick_up ? i19free : i19freeShipping)
43
73
  }
44
74
  }
45
75
  }
@@ -1,10 +1,13 @@
1
1
  import {
2
2
  i19addresses,
3
+ i19favorites,
3
4
  i19hello,
4
5
  i19isNotYou,
5
6
  i19logout,
7
+ i19noSavedFavoritesMsg,
6
8
  i19orders,
7
- i19registration
9
+ i19registration,
10
+ i19subscriptions
8
11
  } from '@ecomplus/i18n'
9
12
 
10
13
  import {
@@ -14,12 +17,14 @@ import {
14
17
 
15
18
  import ecomPassport from '@ecomplus/passport-client'
16
19
  import LoginBlock from '../LoginBlock.vue'
20
+ import RecommendedItems from '../RecommendedItems.vue'
17
21
 
18
22
  export default {
19
23
  name: 'TheAccount',
20
24
 
21
25
  components: {
22
- LoginBlock
26
+ LoginBlock,
27
+ RecommendedItems
23
28
  },
24
29
 
25
30
  props: {
@@ -29,7 +34,18 @@ export default {
29
34
  return {}
30
35
  }
31
36
  },
32
- isOrdersList: Boolean,
37
+ currentTab: {
38
+ type: String,
39
+ validator: function (value) {
40
+ return ['orders', 'favorites', 'subscriptions', 'points', 'account'].includes(value)
41
+ }
42
+ },
43
+ isExternalAuth: {
44
+ type: Boolean,
45
+ default () {
46
+ return Boolean(window.$firebaseConfig && window.$firebaseConfig.authDomain)
47
+ }
48
+ },
33
49
  ecomPassport: {
34
50
  type: Object,
35
51
  default () {
@@ -38,20 +54,30 @@ export default {
38
54
  }
39
55
  },
40
56
 
57
+ data () {
58
+ return {
59
+ favoriteIds: [],
60
+ navTabs: []
61
+ }
62
+ },
63
+
41
64
  computed: {
42
65
  i19addresses: () => i18n(i19addresses),
66
+ i19favorites: () => i18n(i19favorites),
43
67
  i19hello: () => i18n(i19hello),
44
68
  i19isNotYou: () => i18n(i19isNotYou),
45
69
  i19logout: () => i18n(i19logout),
70
+ i19noSavedFavoritesMsg: () => i18n(i19noSavedFavoritesMsg),
46
71
  i19orders: () => i18n(i19orders),
72
+ i19subscriptions: () => i18n(i19subscriptions),
47
73
  i19registration: () => i18n(i19registration),
48
74
 
49
75
  activeTab: {
50
76
  get () {
51
- return this.isOrdersList ? 1 : 0
77
+ return this.currentTab || 'account'
52
78
  },
53
- set (tabIndex) {
54
- this.$emit('update:is-orders-list', tabIndex === 1)
79
+ set (tab) {
80
+ this.$emit('update:current-tab', tab)
55
81
  }
56
82
  },
57
83
 
@@ -70,6 +96,27 @@ export default {
70
96
  },
71
97
 
72
98
  methods: {
99
+ hasTab (tabValue) {
100
+ return this.navTabs.some(tab => tab.value === tabValue)
101
+ },
102
+
103
+ insertSubscriptionTab () {
104
+ const hasSubscriptions = this.hasTab('subscriptions')
105
+ if (this.ecomPassport.checkAuthorization() && !hasSubscriptions) {
106
+ this.ecomPassport.requestApi('/orders.json?transactions.type=recurrence&limit=1&fields=_id')
107
+ .then(({ data }) => {
108
+ const { result } = data
109
+ if (result.length) {
110
+ this.navTabs.push({
111
+ label: this.i19subscriptions,
112
+ value: 'subscriptions'
113
+ })
114
+ }
115
+ })
116
+ .catch(console.error)
117
+ }
118
+ },
119
+
73
120
  login (ecomPassport) {
74
121
  if (ecomPassport.checkAuthorization()) {
75
122
  this.localCustomer = ecomPassport.getCustomer()
@@ -83,5 +130,49 @@ export default {
83
130
  this.$emit('logout')
84
131
  }
85
132
  }
133
+ },
134
+
135
+ watch: {
136
+ customer: {
137
+ handler (customer) {
138
+ const hasPoints = this.hasTab('points')
139
+ if (Array.isArray(customer.loyalty_points_entries) && customer.loyalty_points_entries.length && !hasPoints) {
140
+ this.navTabs.push({
141
+ label: 'Cashback',
142
+ value: 'points'
143
+ })
144
+ }
145
+ },
146
+ immediate: true,
147
+ deep: true
148
+ }
149
+ },
150
+
151
+ created () {
152
+ if (this.isExternalAuth && !this.ecomPassport.checkAuthorization()) {
153
+ window.location.href = '/app/account'
154
+ return
155
+ }
156
+ this.navTabs = [
157
+ {
158
+ label: this.i19registration,
159
+ value: 'account'
160
+ },
161
+ {
162
+ label: this.i19orders,
163
+ value: 'orders'
164
+ },
165
+ {
166
+ label: this.i19favorites,
167
+ value: 'favorites'
168
+ }
169
+ ]
170
+ const { favorites } = this.ecomPassport.getCustomer()
171
+ this.favoriteIds = favorites || []
172
+ this.insertSubscriptionTab()
173
+ this.ecomPassport.on('login', this.insertSubscriptionTab)
174
+ this.$once('hook:beforeDestroy', () => {
175
+ this.ecomPassport.off('login', this.insertSubscriptionTab)
176
+ })
86
177
  }
87
178
  }
@@ -0,0 +1,146 @@
1
+ import {
2
+ i19checkout,
3
+ i19continueShopping,
4
+ i19discount,
5
+ i19emptyCart
6
+ } from '@ecomplus/i18n'
7
+
8
+ import {
9
+ i18n,
10
+ formatMoney
11
+ } from '@ecomplus/utils'
12
+
13
+ import ecomCart from '@ecomplus/shopping-cart'
14
+ import APrices from './../APrices.vue'
15
+ import CartItem from './../CartItem.vue'
16
+ import DiscountApplier from './../DiscountApplier.vue'
17
+ import ShippingCalculator from './../ShippingCalculator.vue'
18
+ import EarnPointsProgress from './../EarnPointsProgress.vue'
19
+ import RecommendedItems from './../RecommendedItems.vue'
20
+
21
+ export default {
22
+ name: 'TheCart',
23
+
24
+ components: {
25
+ APrices,
26
+ CartItem,
27
+ DiscountApplier,
28
+ ShippingCalculator,
29
+ EarnPointsProgress,
30
+ RecommendedItems
31
+ },
32
+
33
+ props: {
34
+ amount: {
35
+ type: Object,
36
+ default () {
37
+ return {}
38
+ }
39
+ },
40
+ checkoutUrl: {
41
+ type: String,
42
+ default: '/app/#/checkout'
43
+ },
44
+ zipCode: String,
45
+ discountCoupon: String,
46
+ modulesPayload: Object,
47
+ ecomCart: {
48
+ type: Object,
49
+ default () {
50
+ return ecomCart
51
+ }
52
+ }
53
+ },
54
+
55
+ data () {
56
+ return {
57
+ localZipCode: this.zipCode,
58
+ canApplyDiscount: false,
59
+ isCouponApplied: false
60
+ }
61
+ },
62
+
63
+ computed: {
64
+ i19checkout: () => i18n(i19checkout),
65
+ i19continueShopping: () => i18n(i19continueShopping),
66
+ i19discount: () => i18n(i19discount),
67
+ i19emptyCart: () => i18n(i19emptyCart),
68
+
69
+ cart () {
70
+ return this.ecomCart.data
71
+ },
72
+
73
+ isValidCart () {
74
+ return this.ecomCart.data.items.find(({ quantity }) => quantity)
75
+ },
76
+
77
+ localDiscountCoupon: {
78
+ get () {
79
+ return this.discountCoupon
80
+ },
81
+ set (couponCode) {
82
+ this.$emit('update:discount-coupon', couponCode)
83
+ }
84
+ }
85
+ },
86
+
87
+ methods: {
88
+ formatMoney,
89
+
90
+ selectShippingService (service) {
91
+ this.$emit('select-shipping', service)
92
+ this.$nextTick(() => {
93
+ this.canApplyDiscount = true
94
+ })
95
+ },
96
+
97
+ setDiscountRule (discountRule) {
98
+ this.$emit('set-discount-rule', discountRule)
99
+ this.$nextTick(() => {
100
+ this.isCouponApplied = Boolean(this.discountCoupon && this.amount.discount)
101
+ })
102
+ }
103
+ },
104
+
105
+ watch: {
106
+ localZipCode (zipCode) {
107
+ this.$emit('update:zip-code', zipCode)
108
+ },
109
+
110
+ canApplyDiscount (canApplyDiscount) {
111
+ if (!canApplyDiscount) {
112
+ this.isCouponApplied = false
113
+ }
114
+ }
115
+ },
116
+
117
+ mounted () {
118
+ this.$nextTick(() => {
119
+ this.canApplyDiscount = !this.localZipCode
120
+ })
121
+ const { ecomCart } = this
122
+ const getNumItems = () => ecomCart.data.items.reduce((numItems, { flags, quantity }) => {
123
+ if (!flags || !flags.includes('freebie')) {
124
+ numItems += quantity
125
+ }
126
+ return numItems
127
+ }, 0)
128
+ let oldNumItems = getNumItems()
129
+ const cartWatcher = () => {
130
+ this.canApplyDiscount = !this.localZipCode
131
+ const numItems = getNumItems()
132
+ if (oldNumItems !== numItems) {
133
+ ecomCart.data.items.forEach(({ _id, quantity, flags }) => {
134
+ if (Array.isArray(flags) && flags.includes('freebie') && quantity === 1) {
135
+ ecomCart.removeItem(_id)
136
+ }
137
+ })
138
+ oldNumItems = numItems
139
+ }
140
+ }
141
+ ecomCart.on('change', cartWatcher)
142
+ this.$once('hook:beforeDestroy', () => {
143
+ ecomCart.off('change', cartWatcher)
144
+ })
145
+ }
146
+ }