@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,10 @@
1
1
  import {
2
- inStock as checkStock,
2
+ i19select,
3
+ i19selectVariation
4
+ } from '@ecomplus/i18n'
5
+
6
+ import {
7
+ i18n,
3
8
  specValueByText as getSpecValueByText,
4
9
  specTextValue as getSpecTextValue,
5
10
  variationsGrids as getVariationsGrids,
@@ -32,7 +37,31 @@ export default {
32
37
  data () {
33
38
  return {
34
39
  selectedOptions: {},
35
- filteredGrids: getVariationsGrids(this.product, null, true)
40
+ filteredGrids: {}
41
+ }
42
+ },
43
+
44
+ computed: {
45
+ i19select: () => i18n(i19select),
46
+ i19selectVariation: () => i18n(i19selectVariation),
47
+
48
+ variationsGrids () {
49
+ return getVariationsGrids(this.product)
50
+ },
51
+
52
+ orderedGrids () {
53
+ return Object.keys(this.variationsGrids)
54
+ },
55
+
56
+ variationFromUrl () {
57
+ if (typeof window === 'object') {
58
+ const urlParams = new URLSearchParams(window.location.search)
59
+ const variationId = urlParams.get('variation_id')
60
+ if (variationId) {
61
+ return variationId
62
+ }
63
+ }
64
+ return null
36
65
  }
37
66
  },
38
67
 
@@ -68,6 +97,11 @@ export default {
68
97
  selectOption (optionText, grid, gridIndex) {
69
98
  const { product, selectedOptions, orderedGrids } = this
70
99
  this.$set(selectedOptions, grid, optionText)
100
+ this.$emit('select-option', {
101
+ gridId: grid,
102
+ gridIndex,
103
+ optionText
104
+ })
71
105
  const filterGrids = {}
72
106
  for (let i = 0; i <= gridIndex; i++) {
73
107
  const grid = orderedGrids[i]
@@ -78,21 +112,21 @@ export default {
78
112
  const nextFilteredGrids = getVariationsGrids(product, filterGrids, true)
79
113
  for (let i = gridIndex + 1; i < orderedGrids.length; i++) {
80
114
  const grid = orderedGrids[i]
81
- this.filteredGrids[grid] = nextFilteredGrids[grid]
115
+ const options = nextFilteredGrids[grid]
116
+ this.filteredGrids[grid] = options
117
+ if (selectedOptions[grid] && !options.includes(selectedOptions[grid])) {
118
+ this.$set(selectedOptions, grid, undefined)
119
+ }
82
120
  }
83
121
  const variations = product.variations.slice(0)
84
122
  for (let i = 0; i < variations.length; i++) {
85
123
  const variation = variations[i]
86
- if (!checkStock(variation)) {
87
- variations.splice(i, 1)
88
- } else {
89
- const { specifications } = variation
90
- for (const grid in specifications) {
91
- if (selectedOptions[grid] !== getSpecTextValue(variation, grid)) {
92
- variations.splice(i, 1)
93
- i--
94
- break
95
- }
124
+ const { specifications } = variation
125
+ for (const grid in specifications) {
126
+ if (selectedOptions[grid] !== getSpecTextValue(variation, grid)) {
127
+ variations.splice(i, 1)
128
+ i--
129
+ break
96
130
  }
97
131
  }
98
132
  }
@@ -100,13 +134,36 @@ export default {
100
134
  }
101
135
  },
102
136
 
103
- computed: {
104
- variationsGrids () {
105
- return getVariationsGrids(this.product)
106
- },
137
+ watch: {
138
+ 'product.variations': {
139
+ handler () {
140
+ this.filteredGrids = getVariationsGrids(this.product, null, true)
141
+ },
142
+ deep: true,
143
+ immediate: true
144
+ }
145
+ },
107
146
 
108
- orderedGrids () {
109
- return Object.keys(this.variationsGrids)
147
+ mounted () {
148
+ if (this.variationFromUrl && Array.isArray(this.product.variations)) {
149
+ const selectedVariation = this.product.variations.find(variation => variation._id === this.variationFromUrl)
150
+ if (selectedVariation) {
151
+ const { specifications } = selectedVariation
152
+ const specs = Object.keys(specifications)
153
+ const nextSpec = (specIndex = 0) => {
154
+ const spec = specs[specIndex]
155
+ if (specs[specIndex] && specifications[spec] && specifications[spec].length === 1) {
156
+ const specText = specifications[spec][0].text
157
+ if (this.variationsGrids[spec].find(option => option === specText)) {
158
+ this.$nextTick(() => {
159
+ this.selectOption(specText, spec, this.orderedGrids.indexOf(spec))
160
+ nextSpec(specIndex + 1)
161
+ })
162
+ }
163
+ }
164
+ }
165
+ nextSpec()
166
+ }
110
167
  }
111
168
  }
112
169
  }
@@ -0,0 +1,175 @@
1
+ import {
2
+ i19buyKit,
3
+ i19maxQuantity,
4
+ i19minQuantity
5
+ } from '@ecomplus/i18n'
6
+
7
+ import { i18n } from '@ecomplus/utils'
8
+
9
+ import ecomCart from '@ecomplus/shopping-cart'
10
+ import ALink from '../ALink.vue'
11
+ import AAlert from '../AAlert.vue'
12
+
13
+ export default {
14
+ name: 'QuantitySelector',
15
+
16
+ components: {
17
+ ALink,
18
+ AAlert
19
+ },
20
+
21
+ props: {
22
+ items: {
23
+ type: Array,
24
+ required: true
25
+ },
26
+ min: {
27
+ type: Number,
28
+ default: 1
29
+ },
30
+ max: Number,
31
+ slug: String,
32
+ buyText: String,
33
+ kitProductId: String,
34
+ kitName: String,
35
+ kitPrice: Number,
36
+ canAddToCart: {
37
+ type: Boolean,
38
+ default: true
39
+ },
40
+ hasBuyButton: {
41
+ type: Boolean,
42
+ default: true
43
+ }
44
+ },
45
+
46
+ data () {
47
+ return {
48
+ selectedQnts: this.items.reduce((selectedQnts, item) => {
49
+ selectedQnts[item._id] = item.quantity || 0
50
+ return selectedQnts
51
+ }, {}),
52
+ hasMinAlert: false,
53
+ hasMaxAlert: false,
54
+ alertVariant: 'warning'
55
+ }
56
+ },
57
+
58
+ computed: {
59
+ i19maxQuantity: () => i18n(i19maxQuantity),
60
+ i19minQuantity: () => i18n(i19minQuantity),
61
+
62
+ totalQuantity () {
63
+ let total = 0
64
+ const { selectedQnts } = this
65
+ Object.keys(selectedQnts).forEach(key => {
66
+ if (selectedQnts[key]) {
67
+ total += selectedQnts[key]
68
+ }
69
+ })
70
+ return total
71
+ },
72
+
73
+ remainingQuantity () {
74
+ return this.max
75
+ ? this.max - this.totalQuantity
76
+ : 9999999
77
+ },
78
+
79
+ strBuy () {
80
+ return this.buyText || i18n(i19buyKit)
81
+ }
82
+ },
83
+
84
+ methods: {
85
+ checkInStock (item) {
86
+ const maxQuantity = item.max_quantity
87
+ return typeof maxQuantity === 'number' && maxQuantity >= 0
88
+ ? maxQuantity
89
+ : 9999999
90
+ },
91
+
92
+ changeQnt (item, qntDiff, ev) {
93
+ const { selectedQnts, remainingQuantity } = this
94
+ const lastQnt = selectedQnts[item._id]
95
+ let newQnt
96
+ if (qntDiff) {
97
+ newQnt = selectedQnts[item._id] + qntDiff
98
+ } else if (ev) {
99
+ selectedQnts[item._id] = ev.target.value.replace(/\D/g, '')
100
+ newQnt = parseInt(selectedQnts[item._id], 10)
101
+ }
102
+ if (this.items.length === 1 && this.min > newQnt) {
103
+ newQnt = this.min
104
+ }
105
+ if (newQnt > 0) {
106
+ if (item.min_quantity > newQnt) {
107
+ newQnt = item.min_quantity
108
+ } else {
109
+ const itemMaxQnt = item.max_quantity !== undefined ? item.max_quantity : 9999999
110
+ const maxQnt = Math.min(itemMaxQnt, lastQnt + remainingQuantity)
111
+ if (maxQnt < newQnt) {
112
+ this.alertVariant = 'info'
113
+ this.hasMaxAlert = true
114
+ newQnt = maxQnt
115
+ }
116
+ }
117
+ selectedQnts[item._id] = newQnt
118
+ } else {
119
+ selectedQnts[item._id] = 0
120
+ }
121
+ this.$emit('set-quantity', {
122
+ item,
123
+ quantity: selectedQnts[item._id]
124
+ })
125
+ },
126
+
127
+ buy () {
128
+ this.alertVariant = 'warning'
129
+ if (this.totalQuantity >= this.min) {
130
+ if (this.max === undefined || this.totalQuantity <= this.max) {
131
+ const items = []
132
+ const composition = this.items.map(item => ({
133
+ _id: item.product_id,
134
+ variation_id: item.variation_id,
135
+ quantity: this.selectedQnts[item._id]
136
+ }))
137
+ this.items.forEach(item => {
138
+ const quantity = this.selectedQnts[item._id]
139
+ if (quantity > 0) {
140
+ const newItem = { ...item, quantity }
141
+ delete newItem.customizations
142
+ if (this.kitProductId) {
143
+ newItem.kit_product = {
144
+ _id: this.kitProductId,
145
+ name: this.kitName,
146
+ pack_quantity: this.totalQuantity,
147
+ price: this.kitPrice,
148
+ composition
149
+ }
150
+ }
151
+ if (this.slug) {
152
+ newItem.slug = this.slug
153
+ }
154
+ items.push(newItem)
155
+ if (this.canAddToCart) {
156
+ ecomCart.addItem(newItem)
157
+ }
158
+ }
159
+ })
160
+ this.$emit('buy', { items })
161
+ } else {
162
+ this.hasMaxAlert = true
163
+ }
164
+ } else {
165
+ this.hasMinAlert = true
166
+ }
167
+ }
168
+ },
169
+
170
+ created () {
171
+ if (this.max < this.items.length) {
172
+ this.items.forEach(item => this.changeQnt(item))
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,178 @@
1
+ import {
2
+ i19add,
3
+ i19seeMore,
4
+ i19weRecommendToYou
5
+ } from '@ecomplus/i18n'
6
+
7
+ import {
8
+ i18n,
9
+ recommendedIds as getRecommendedIds
10
+ } from '@ecomplus/utils'
11
+
12
+ import { graphs } from '@ecomplus/client'
13
+ import EcomSearch from '@ecomplus/search-engine'
14
+ import ecomCart from '@ecomplus/shopping-cart'
15
+ import { isMobile } from '@ecomplus/storefront-twbs'
16
+ import addIdleCallback from './helpers/add-idle-callback'
17
+ import ProductCard from '../ProductCard.vue'
18
+
19
+ export default {
20
+ name: 'RecommendedItems',
21
+
22
+ components: {
23
+ ProductCard
24
+ },
25
+
26
+ props: {
27
+ pageSize: {
28
+ type: Number,
29
+ default: !isMobile ? 4 : 2
30
+ },
31
+ sortOrder: {
32
+ type: String,
33
+ default: 'sales'
34
+ },
35
+ canLoadMore: {
36
+ type: Boolean,
37
+ default: true
38
+ },
39
+ rowClassName: {
40
+ type: String,
41
+ default: 'row no-gutters'
42
+ },
43
+ colClassName: {
44
+ type: String,
45
+ default: 'col-6 col-md-4 col-lg-3'
46
+ },
47
+ productCardProps: {
48
+ type: Object,
49
+ default () {
50
+ return {
51
+ isSmall: true,
52
+ buyText: i18n(i19add),
53
+ installmentsOption: {},
54
+ discountOption: {}
55
+ }
56
+ }
57
+ },
58
+ ecomCart: {
59
+ type: Object,
60
+ default () {
61
+ return ecomCart
62
+ }
63
+ },
64
+ productIds: {
65
+ type: Array,
66
+ default () {
67
+ return []
68
+ }
69
+ },
70
+ defaultMatchType: {
71
+ type: String,
72
+ default: (typeof window === 'object' && window.ecomRecommendationsType) || 'recommended'
73
+ }
74
+ },
75
+
76
+ data () {
77
+ return {
78
+ ecomSearch: new EcomSearch()
79
+ .mergeFilter({
80
+ range: {
81
+ quantity: {
82
+ gt: 0
83
+ }
84
+ }
85
+ })
86
+ .mergeFilter({
87
+ term: {
88
+ available: true
89
+ }
90
+ }),
91
+ pageNumber: 1,
92
+ items: []
93
+ }
94
+ },
95
+
96
+ computed: {
97
+ i19seeMore: () => i18n(i19seeMore),
98
+ i19weRecommendToYou: () => i18n(i19weRecommendToYou)
99
+ },
100
+
101
+ methods: {
102
+ fetchItems () {
103
+ delete this.ecomSearch.dsl.aggs
104
+ this.ecomSearch.setPageNumber(this.pageNumber).fetch().then(() => {
105
+ this.items = this.items.concat(this.ecomSearch.getItems())
106
+ this.totalCount = this.ecomSearch.getTotalCount()
107
+ if (this.totalCount) {
108
+ this.$emit('recommend-items', {
109
+ items: this.items,
110
+ totalCount: this.totalCount
111
+ })
112
+ }
113
+ }).finally(() => {
114
+ this.$emit('fetched')
115
+ })
116
+ }
117
+ },
118
+
119
+ created () {
120
+ const fetchRecommendations = (matchType = this.defaultMatchType) => {
121
+ const promises = []
122
+ const items = this.ecomCart.data.items.sort((a, b) => a.quantity > b.quantity ? -1 : 1)
123
+ for (let i = 0; i < items.length && i <= 4; i++) {
124
+ promises.push(graphs({ url: `/products/${items[i].product_id}/${matchType}.json` }))
125
+ }
126
+ Promise.allSettled(promises).then(results => {
127
+ const productIds = []
128
+ results.forEach(({ status, value }) => {
129
+ if (status === 'fulfilled') {
130
+ getRecommendedIds(value.data).forEach(productId => {
131
+ if (
132
+ !productIds.includes(productId) &&
133
+ !this.ecomCart.data.items.find(item => item.product_id === productId)
134
+ ) {
135
+ productIds.push(productId)
136
+ }
137
+ })
138
+ }
139
+ })
140
+ if (productIds.length) {
141
+ this.ecomSearch.setProductIds(productIds.slice(0, 24))
142
+ this.fetchItems()
143
+ } else if (matchType === 'recommended') {
144
+ fetchRecommendations('related')
145
+ }
146
+ })
147
+ }
148
+ if (this.productIds.length) {
149
+ this.ecomSearch.setProductIds(this.productIds)
150
+ this.totalCount = this.items.length
151
+ this.fetchItems()
152
+ } else {
153
+ addIdleCallback(() => {
154
+ fetchRecommendations()
155
+ })
156
+ }
157
+ },
158
+
159
+ watch: {
160
+ pageSize: {
161
+ handler (pageSize) {
162
+ this.ecomSearch.setPageSize(pageSize)
163
+ },
164
+ immediate: true
165
+ },
166
+
167
+ sortOrder: {
168
+ handler (sortOrder) {
169
+ this.ecomSearch.setSortOrder(sortOrder)
170
+ },
171
+ immediate: true
172
+ },
173
+
174
+ pageNumber () {
175
+ this.fetchItems()
176
+ }
177
+ }
178
+ }