@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
@@ -0,0 +1,246 @@
1
+ // import { i19buyTogetherWith } from '@ecomplus/i18n'
2
+ import { formatMoney, price as getPrice, recommendedIds } from '@ecomplus/utils'
3
+ import { modules, graphs } from '@ecomplus/client'
4
+ import ecomCart from '@ecomplus/shopping-cart'
5
+ import EcomSearch from '@ecomplus/search-engine'
6
+ import APrices from './../APrices.vue'
7
+ import ProductCard from './../ProductCard.vue'
8
+
9
+ const storefront = (typeof window === 'object' && window.storefront) || {}
10
+ const getContextBody = () => (storefront.context && storefront.context.body) || {}
11
+ const sanitizeProductBody = body => {
12
+ const product = Object.assign({}, body)
13
+ delete product.body_html
14
+ delete product.body_text
15
+ delete product.specifications
16
+ delete product.inventory_records
17
+ delete product.price_change_records
18
+ return product
19
+ }
20
+
21
+ export default {
22
+ name: 'BuyTogether',
23
+
24
+ components: {
25
+ APrices,
26
+ ProductCard
27
+ },
28
+
29
+ props: {
30
+ baseProduct: {
31
+ type: Object,
32
+ default () {
33
+ return getContextBody()
34
+ }
35
+ },
36
+ ecomCart: {
37
+ type: Object,
38
+ default () {
39
+ return ecomCart
40
+ }
41
+ },
42
+ productCardProps: {
43
+ type: Object,
44
+ default () {
45
+ return {
46
+ isSmall: true
47
+ }
48
+ }
49
+ },
50
+ fallbackMatchType: {
51
+ type: String,
52
+ default: (typeof window === 'object' && window.ecomRecommendationsType) || 'recommended'
53
+ }
54
+ },
55
+
56
+ data () {
57
+ return {
58
+ ecomSearch: new EcomSearch()
59
+ .mergeFilter({
60
+ range: {
61
+ quantity: {
62
+ gt: 0
63
+ }
64
+ }
65
+ })
66
+ .mergeFilter({
67
+ term: {
68
+ available: true
69
+ }
70
+ }),
71
+ hasLoadedIds: false,
72
+ hasLoadedItems: false,
73
+ productQnts: {},
74
+ recommendedItems: [],
75
+ discount: 0,
76
+ discountType: 'fixed',
77
+ discountValue: 0
78
+ }
79
+ },
80
+
81
+ computed: {
82
+ i19buyTogether: () => 'Compre junto',
83
+ i19buyTogetherWith: () => 'Compre junto com',
84
+
85
+ items () {
86
+ return [
87
+ this.baseProduct,
88
+ ...this.recommendedItems
89
+ ]
90
+ },
91
+
92
+ productIds () {
93
+ return Object.keys(this.productQnts)
94
+ },
95
+
96
+ relatedProducts () {
97
+ const relatedProducts = this.baseProduct.related_products && this.baseProduct.related_products[0]
98
+ return relatedProducts && relatedProducts.product_ids.length
99
+ ? relatedProducts.product_ids
100
+ : []
101
+ },
102
+
103
+ subtotal () {
104
+ return this.items.reduce((acc, item) => {
105
+ return acc + (this.productQnts[item._id] || 1) * getPrice(item)
106
+ }, 0)
107
+ },
108
+
109
+ canAddToCart () {
110
+ return !this.items.find((item) => {
111
+ return item.variations || item.customizations || item.kit_composition
112
+ })
113
+ }
114
+ },
115
+
116
+ methods: {
117
+ formatMoney,
118
+
119
+ buy () {
120
+ const discountFactor = (this.subtotal - this.discount) / this.subtotal
121
+ this.items.forEach((item) => {
122
+ const cartItem = this.ecomCart.parseProduct({
123
+ ...item,
124
+ base_price: getPrice(item),
125
+ price: getPrice(item) * discountFactor,
126
+ price_effective_date: {}
127
+ })
128
+ cartItem.quantity = (this.productQnts[item._id] || 1)
129
+ cartItem.keep_item_quantity = true
130
+ this.ecomCart.addItem(cartItem)
131
+ })
132
+ },
133
+
134
+ calcDiscount () {
135
+ if (this.discountType === 'fixed') {
136
+ this.discount = this.discountValue
137
+ } else {
138
+ this.discount = this.subtotal * this.discountValue / 100
139
+ }
140
+ },
141
+
142
+ setProductQnts (productsIds) {
143
+ if (productsIds.length) {
144
+ const productQnts = {}
145
+ productsIds.slice(0, 3).forEach(id => {
146
+ productQnts[id] = 1
147
+ })
148
+ this.productQnts = productQnts
149
+ }
150
+ },
151
+
152
+ fetchItems () {
153
+ if (!this.productIds.length) {
154
+ this.hasLoadedItems = true
155
+ return
156
+ }
157
+ this.ecomSearch.setProductIds(this.productIds)
158
+ delete this.ecomSearch.dsl.aggs
159
+ this.ecomSearch.fetch().then(() => {
160
+ this.recommendedItems = this.recommendedItems.concat(this.ecomSearch.getItems())
161
+ }).finally(() => {
162
+ this.hasLoadedItems = true
163
+ })
164
+ }
165
+ },
166
+
167
+ watch: {
168
+ subtotal: {
169
+ handler (subtotal, oldSubtotal) {
170
+ if (subtotal !== oldSubtotal) {
171
+ this.calcDiscount()
172
+ }
173
+ },
174
+ immediate: true
175
+ }
176
+ },
177
+
178
+ created () {
179
+ if (this.baseProduct && this.baseProduct._id) {
180
+ const cartItem = ecomCart.parseProduct(sanitizeProductBody(this.baseProduct))
181
+ const subtotal = getPrice(cartItem) * cartItem.quantity
182
+ modules({
183
+ url: '/apply_discount.json',
184
+ method: 'POST',
185
+ data: {
186
+ amount: {
187
+ subtotal,
188
+ total: subtotal,
189
+ discount: 0
190
+ },
191
+ items: [cartItem]
192
+ }
193
+ }).then(({ data }) => {
194
+ for (let i = 0; i < data.result.length; i++) {
195
+ const { validated, error, response } = data.result[i]
196
+ if (validated && !error && response.buy_together) {
197
+ const buyTogether = response.buy_together.sort((a, b) => {
198
+ if (a.products && a.products.length) {
199
+ if (!b.products || !b.products.length) {
200
+ return -1
201
+ }
202
+ if (
203
+ a.products.length <= b.products.length &&
204
+ a.discount.value >= b.discount.value
205
+ ) {
206
+ return -1
207
+ }
208
+ return 0
209
+ }
210
+ return 1
211
+ })
212
+ if (buyTogether[0]) {
213
+ const { products, discount } = buyTogether[0]
214
+ this.productQnts = products || []
215
+ this.discountType = discount.type
216
+ this.discountValue = discount.value
217
+ this.$nextTick(() => {
218
+ this.calcDiscount()
219
+ })
220
+ }
221
+ }
222
+ }
223
+ }).finally(() => {
224
+ this.hasLoadedIds = true
225
+ this.$nextTick(() => {
226
+ if (!this.productIds.length) {
227
+ if (this.relatedProducts.length) {
228
+ this.setProductQnts(this.relatedProducts)
229
+ this.fetchItems()
230
+ } else if (this.fallbackMatchType) {
231
+ graphs({ url: `/products/${this.baseProduct._id}/${this.fallbackMatchType}.json` })
232
+ .then(({ data }) => {
233
+ this.setProductQnts(recommendedIds(data))
234
+ this.$nextTick(() => {
235
+ this.fetchItems()
236
+ })
237
+ })
238
+ }
239
+ } else {
240
+ this.fetchItems()
241
+ }
242
+ })
243
+ })
244
+ }
245
+ }
246
+ }
@@ -1,4 +1,6 @@
1
1
  import {
2
+ i19freebie,
3
+ i19outOfStock,
2
4
  i19quantity,
3
5
  i19remove
4
6
  } from '@ecomplus/i18n'
@@ -13,13 +15,15 @@ import {
13
15
  import ecomCart from '@ecomplus/shopping-cart'
14
16
  import ALink from '../ALink.vue'
15
17
  import APicture from '../APicture.vue'
18
+ import ItemCustomizations from '../ItemCustomizations.vue'
16
19
 
17
20
  export default {
18
21
  name: 'CartItem',
19
22
 
20
23
  components: {
21
24
  ALink,
22
- APicture
25
+ APicture,
26
+ ItemCustomizations
23
27
  },
24
28
 
25
29
  props: {
@@ -49,6 +53,8 @@ export default {
49
53
  },
50
54
 
51
55
  computed: {
56
+ i19freebie: () => i18n(i19freebie),
57
+ i19outOfStock: () => i18n(i19outOfStock),
52
58
  i19quantity: () => i18n(i19quantity),
53
59
  i19remove: () => i18n(i19remove),
54
60
 
@@ -57,15 +63,49 @@ export default {
57
63
  },
58
64
 
59
65
  price () {
60
- return getPrice(this.item)
66
+ return this.item.final_price || getPrice(this.item)
61
67
  },
62
68
 
63
69
  img () {
64
- return getImg(this.item, null, 'small')
70
+ return getImg(this.item.picture || this.item, null, 'small')
65
71
  },
66
72
 
67
73
  name () {
68
- const { name } = this.item
74
+ return this.formatName(this.item.name)
75
+ },
76
+
77
+ isFreebie () {
78
+ return Array.isArray(this.item.flags)
79
+ ? this.item.flags.includes('freebie')
80
+ : false
81
+ },
82
+
83
+ isIntegerQnt () {
84
+ return Number.isInteger(this.maxQuantity) && Number.isInteger(this.quantity)
85
+ },
86
+
87
+ minQuantity () {
88
+ const minQuantity = this.item.min_quantity
89
+ return typeof minQuantity === 'number' && minQuantity >= 0
90
+ ? minQuantity
91
+ : 1
92
+ },
93
+
94
+ maxQuantity () {
95
+ if (this.item.available === false) {
96
+ return 0
97
+ }
98
+ const maxQuantity = this.item.max_quantity
99
+ return typeof maxQuantity === 'number' && maxQuantity >= 0
100
+ ? maxQuantity
101
+ : 9999999
102
+ }
103
+ },
104
+
105
+ methods: {
106
+ formatMoney,
107
+
108
+ formatName (name) {
69
109
  if (name) {
70
110
  if (name.length <= this.nameMaxLength) {
71
111
  return name
@@ -73,15 +113,21 @@ export default {
73
113
  return `${name.substr(0, this.nameMaxLength)}...`
74
114
  }
75
115
  }
76
- }
77
- },
116
+ },
78
117
 
79
- methods: {
80
- formatMoney,
118
+ validateQuantity () {
119
+ if (this.minQuantity <= this.maxQuantity) {
120
+ if (this.quantity < this.minQuantity) {
121
+ this.quantity = this.minQuantity
122
+ } else if (this.quantity > this.maxQuantity) {
123
+ this.quantity = this.maxQuantity
124
+ }
125
+ }
126
+ },
81
127
 
82
128
  updateInputType () {
83
- this.canInputSelect = Number.isInteger(this.quantity) &&
84
- this.quantity > 0 && this.quantity <= 10
129
+ this.validateQuantity()
130
+ this.canInputSelect = this.isIntegerQnt && this.quantity > 0 && this.quantity <= 10
85
131
  },
86
132
 
87
133
  remove () {
@@ -100,7 +146,9 @@ export default {
100
146
  watch: {
101
147
  'item.quantity': {
102
148
  handler (qnt) {
103
- this.quantity = qnt || 0
149
+ if (this.quantity || qnt > 1) {
150
+ this.quantity = qnt || 0
151
+ }
104
152
  },
105
153
  immediate: true
106
154
  },
@@ -110,18 +158,23 @@ export default {
110
158
  qnt = 0
111
159
  }
112
160
  if (qnt !== this.item.quantity) {
113
- const quantityToAdd = qnt - oldQnt
161
+ const quantityToAdd = qnt - this.item.quantity
114
162
  this.$emit('increase', {
115
163
  quantityToAdd,
116
164
  newQuantity: qnt
117
165
  })
118
166
  if (this.itemId && this.canUpdateCart) {
119
- ecomCart.increaseItemQnt(this.itemId, quantityToAdd)
167
+ const item = ecomCart.increaseItemQnt(this.itemId, quantityToAdd)
168
+ if (this.isFreebie) {
169
+ item.flags = item.flags.filter(flag => !flag.startsWith('freebie'))
170
+ }
120
171
  }
121
172
  }
122
173
  if (qnt > 10 && oldQnt <= 10) {
123
174
  this.$nextTick(() => {
124
- this.$refs.input.focus()
175
+ if (this.$refs.input) {
176
+ this.$refs.input.focus()
177
+ }
125
178
  })
126
179
  }
127
180
  }
@@ -18,6 +18,7 @@ import ALink from '../ALink.vue'
18
18
  import ABackdrop from '../ABackdrop.vue'
19
19
  import APrices from '../APrices.vue'
20
20
  import CartItem from '../CartItem.vue'
21
+ import ShippingCalculator from '../ShippingCalculator.vue'
21
22
 
22
23
  export default {
23
24
  name: 'CartQuickview',
@@ -26,7 +27,8 @@ export default {
26
27
  ALink,
27
28
  ABackdrop,
28
29
  APrices,
29
- CartItem
30
+ CartItem,
31
+ ShippingCalculator
30
32
  },
31
33
 
32
34
  props: {
@@ -34,6 +36,7 @@ export default {
34
36
  type: Boolean,
35
37
  default: true
36
38
  },
39
+ hasShippingCalculator: Boolean,
37
40
  checkoutUrl: {
38
41
  type: String,
39
42
  default: '/app/#/checkout'
@@ -54,6 +57,12 @@ export default {
54
57
  }
55
58
  },
56
59
 
60
+ data () {
61
+ return {
62
+ selectedShippingPrice: 0
63
+ }
64
+ },
65
+
57
66
  computed: {
58
67
  i19checkout: () => i18n(i19checkout),
59
68
  i19close: () => i18n(i19close),
@@ -65,6 +74,10 @@ export default {
65
74
 
66
75
  cart () {
67
76
  return this.ecomCart.data
77
+ },
78
+
79
+ total () {
80
+ return this.cart.subtotal + this.selectedShippingPrice
68
81
  }
69
82
  },
70
83
 
@@ -76,6 +89,12 @@ export default {
76
89
  'update:is-visible',
77
90
  typeof isVisible === 'boolean' ? isVisible : !this.isVisible
78
91
  )
92
+ },
93
+
94
+ selectShippingService (service) {
95
+ this.selectedShippingPrice = service.shipping_line
96
+ ? service.shipping_line.total_price
97
+ : 0
79
98
  }
80
99
  },
81
100