@nuskin/product-components 3.17.0 → 3.17.2

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.
package/docs/CHANGELOG.md CHANGED
@@ -1 +1 @@
1
- # [3.17.0](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.16.1...v3.17.0) (2024-06-07)
1
+ ## [3.17.2](https://code.tls.nuskin.io/ns-am/ux/product-components/compare/v3.17.1...v3.17.2) (2024-06-17)
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.4",
4
- "serialNumber": "urn:uuid:0c911df0-c46a-4f6b-8cea-c8df6a114fa2",
4
+ "serialNumber": "urn:uuid:d791f546-6b5a-47e2-b278-d5d586e7caee",
5
5
  "version": 1,
6
6
  "metadata": {
7
- "timestamp": "2024-06-07T05:27:05Z",
7
+ "timestamp": "2024-06-17T16:53:01Z",
8
8
  "tools": [
9
9
  {
10
10
  "vendor": "GitLab",
@@ -21,45 +21,20 @@ import {
21
21
  AdrService,
22
22
  FavoritesService,
23
23
  ProductStatus,
24
- QualificationService as OldQualificationService,
25
24
  Product as ShopProduct,
26
25
  EquinoxCartService
27
26
  } from "@nuskin/ns-shop";
28
- import { default as NewQualificationService } from "@nuskin/exclusive-offer-sdk";
29
- import { retrieveTogglesBasedOnEnvironment } from "@nuskin/ns-feature-flags";
27
+ import QualificationService from "@nuskin/exclusive-offer-sdk";
30
28
  import webLoyalty from "@nuskin/ns-loyalty-web";
31
29
  import { getProp, getFullUrl } from "@nuskin/ns-common-lib";
32
30
  import { PriceType } from "@nuskin/ns-product-lib";
33
31
  import { ProductDataService } from "@nuskin/ns-product";
34
32
  import { waitForConfig } from "../services/configHelper";
33
+ import { fetchPromotionalPrices } from "../services/EQPromotionService";
35
34
 
36
35
  const PENDING_FAVORITE = "pendingFavorite";
37
36
  const KEY_BREADCRUMB_BACK = "breadcrumbBack";
38
-
39
- let flagsPromise = null;
40
- let featureFlags = false;
41
- const checkFlag = async flag => {
42
- if (!featureFlags && !flagsPromise) {
43
- flagsPromise = retrieveTogglesBasedOnEnvironment().then(
44
- flagsFromServer => (featureFlags = flagsFromServer)
45
- );
46
- }
47
-
48
- if (!featureFlags) {
49
- await flagsPromise;
50
- }
51
-
52
- return featureFlags.has(flag);
53
- };
54
-
55
- const getQualificationService = async () => {
56
- const equinoxMarket = (await getConfiguration(["Equinox_Markets"]))
57
- .Equinox_Markets;
58
- return equinoxMarket.active ||
59
- (await checkFlag("cx15_use_new_exclusive_offer_api"))
60
- ? NewQualificationService
61
- : OldQualificationService;
62
- };
37
+ const BUNDLE = "BUNDLE";
63
38
 
64
39
  /**
65
40
  * NsProductMixin
@@ -216,6 +191,8 @@ const NsProductMixin = {
216
191
  * Active poduct data
217
192
  */
218
193
  product: undefined,
194
+ promotionPrice: null,
195
+ promotionOriginalPrice: null,
219
196
  /**
220
197
  * Quantity of product selected
221
198
  */
@@ -550,7 +527,7 @@ const NsProductMixin = {
550
527
  /**
551
528
  * Sets product to new selection
552
529
  */
553
- setActiveProduct() {
530
+ async setActiveProduct() {
554
531
  if (this.baseProduct && this.baseProduct.isBase()) {
555
532
  let activeProduct = this.variants.find(
556
533
  variantProduct => variantProduct.sku === this.activeSku
@@ -619,6 +596,30 @@ const NsProductMixin = {
619
596
  //console.error(err);
620
597
  }
621
598
 
599
+ let isBundle = false;
600
+ if (this.product.childSkus && this.product.childSkus.length) {
601
+ this.product.childSkus.forEach(product => {
602
+ if (product.type === BUNDLE) {
603
+ isBundle = true;
604
+ }
605
+ });
606
+ }
607
+ if (isBundle) {
608
+ const promotion = await fetchPromotionalPrices(this.product);
609
+ if (promotion) {
610
+ if (promotion.price) {
611
+ this.promotionPrice = promotion.price;
612
+ this.price = promotion.price;
613
+ this.product.setPrice(promotion.price);
614
+ }
615
+ if (promotion.originalPrice) {
616
+ this.promotionOriginalPrice = promotion.originalPrice;
617
+ this.originalPrice = promotion.originalPrice;
618
+ }
619
+ }
620
+ //this.$emit("quantity-select", this.selectedQuantity);
621
+ }
622
+
622
623
  this.setProductPricingAndAvailability();
623
624
  this.$emit("product-update", this.product);
624
625
  },
@@ -644,25 +645,32 @@ const NsProductMixin = {
644
645
  if (this.product) {
645
646
  // WARNING: There's a instanceof check on CartService.getAddToCartQty for the ns-shop Product, previously different
646
647
  const shopProduct = new ShopProduct(this.product);
647
- // check if a user is qualified to purchase the product
648
- CartService.getAddToCartQty(shopProduct)
649
- .then(productQuantity => {
650
- // WARNING: this.product is volatile here. re-check is using this.product.
651
- this.maxQuantity = Math.min(
652
- shopProduct.maxQuantity,
653
- shopProduct.availableQuantity,
654
- productQuantity
655
- );
656
- })
657
- .catch(error => {
658
- this.maxQuantity = 0;
659
- console.error("Failed to retrieve add to cart quantity.", error);
660
- })
661
- .finally(() => {
662
- this.setStatus();
663
- this.checkedQualifications = true;
664
- this.emitAvailability();
665
- });
648
+
649
+ getConfiguration(["Equinox_Markets"]).then(({ Equinox_Markets }) => {
650
+ const { active } = Equinox_Markets;
651
+
652
+ const cartService = active ? EquinoxCartService : CartService;
653
+ // check if a user is qualified to purchase the product
654
+ cartService
655
+ .getAddToCartQty(shopProduct)
656
+ .then(productQuantity => {
657
+ // WARNING: this.product is volatile here. re-check is using this.product.
658
+ this.maxQuantity = Math.min(
659
+ shopProduct.maxQuantity,
660
+ shopProduct.availableQuantity,
661
+ productQuantity
662
+ );
663
+ })
664
+ .catch(error => {
665
+ this.maxQuantity = 0;
666
+ console.error("Failed to retrieve add to cart quantity.", error);
667
+ })
668
+ .finally(() => {
669
+ this.setStatus();
670
+ this.checkedQualifications = true;
671
+ this.emitAvailability();
672
+ });
673
+ });
666
674
  }
667
675
  },
668
676
 
@@ -727,9 +735,9 @@ const NsProductMixin = {
727
735
  this.exclusiveOfferMessage = "";
728
736
  if (this.isExclusive) {
729
737
  if (this.isLoggedIn) {
730
- const productQualification = await (
731
- await getQualificationService()
732
- ).getQualification(this.activeSku);
738
+ const productQualification = await QualificationService.getQualification(
739
+ this.activeSku
740
+ );
733
741
 
734
742
  let cartQuantity = 0;
735
743
 
@@ -759,10 +767,10 @@ const NsProductMixin = {
759
767
  cartQuantity = (cartItem || {}).qty || 0;
760
768
  } else {
761
769
  const cartData =
762
- (await (
763
- await getQualificationService()
764
- ).convertCartDataToBaseSkus(CartService.getItemData(), true)) ||
765
- {};
770
+ (await QualificationService.convertCartDataToBaseSkus(
771
+ CartService.getItemData(),
772
+ true
773
+ )) || {};
766
774
  const cartItem = cartData[this.baseSku] || {};
767
775
  cartQuantity = cartItem.qty || 0;
768
776
  }
@@ -840,7 +848,7 @@ const NsProductMixin = {
840
848
  /**
841
849
  * Sets sales event pricing if there is an active event for this sku, or normal pricing if not
842
850
  */
843
- setProductPricing() {
851
+ async setProductPricing() {
844
852
  const equinoxMarketsConfig = getCachedConfiguration("Equinox_Markets");
845
853
  const isEquinoxEnabled =
846
854
  equinoxLocalStorage.isEquinoxEnabled() && equinoxMarketsConfig.active;
@@ -934,6 +942,24 @@ const NsProductMixin = {
934
942
  this.isSale = false;
935
943
  this.setStandardPricing();
936
944
  }
945
+
946
+ let isBundle = false;
947
+ if (this.product.childSkus && this.product.childSkus.length) {
948
+ this.product.childSkus.forEach(product => {
949
+ if (product.type === BUNDLE) {
950
+ isBundle = true;
951
+ }
952
+ });
953
+ }
954
+ if (isBundle && isEquinoxEnabled) {
955
+ if (this.promotionPrice) {
956
+ this.price = this.promotionPrice;
957
+ this.product.setPrice(this.promotionPrice);
958
+ }
959
+ if (this.promotionOriginalPrice) {
960
+ this.originalPrice = this.promotionOriginalPrice;
961
+ }
962
+ }
937
963
  },
938
964
 
939
965
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/product-components",
3
- "version": "3.17.0",
3
+ "version": "3.17.2",
4
4
  "description": "Nu Skin Product Components",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,138 @@
1
+ import axios from "axios";
2
+ import { UrlService } from "@nuskin/ns-util";
3
+ import { getConfiguration } from "@nuskin/configuration-sdk";
4
+
5
+ /**
6
+ * To get the promontional prices from equinox storefront
7
+ *
8
+ */
9
+
10
+ export const fetchPromotionalPrices = async product => {
11
+ try {
12
+ const payload = buildPayload(product);
13
+ const options = {
14
+ method: "POST",
15
+ endpoint: `catalogs/products/${product.sku}/promotions`,
16
+ data: payload
17
+ };
18
+
19
+ const { data } = await _equinoxRequest(options);
20
+ return {
21
+ price: data.unitValue.priceAfterDiscount,
22
+ originalPrice: data.unitValue.originalPrice
23
+ };
24
+ } catch (err) {
25
+ console.log("Error while fetching promotional prices");
26
+ return {};
27
+ }
28
+ };
29
+
30
+ const buildPayload = product => {
31
+ const payload = {
32
+ productId: product.sku,
33
+ categoryId: ["all_products"],
34
+ quantity: 1,
35
+ skus: [],
36
+ isSubscriptionIncludedInPromotion: false,
37
+ isFromBrowse: true
38
+ };
39
+
40
+ try {
41
+ let personalOffer = sessionStorage.getItem("personalOffer");
42
+ if (personalOffer !== null) {
43
+ personalOffer = JSON.parse(personalOffer);
44
+ }
45
+ const matchedProduct = personalOffer.products.filter(
46
+ offerProduct => offerProduct.sku === product.sku
47
+ );
48
+ const selectedVariants =
49
+ matchedProduct &&
50
+ matchedProduct.length > 0 &&
51
+ matchedProduct[0].variantSelected
52
+ ? matchedProduct[0].variantSelected
53
+ : null;
54
+
55
+ const skus = [];
56
+ if (product && product.childSkus) {
57
+ product.childSkus.forEach(childSku => {
58
+ if (childSku.type === "MANDATORY") {
59
+ skus.push({
60
+ skuId:
61
+ selectedVariants !== null && selectedVariants[childSku.productId]
62
+ ? selectedVariants[childSku.productId]
63
+ : childSku.skuId,
64
+ productId: childSku.productId,
65
+ quantity: childSku.skuQuantity,
66
+ type: "bundle"
67
+ });
68
+ }
69
+ });
70
+ }
71
+
72
+ payload.skus = skus;
73
+ payload.quantity =
74
+ matchedProduct && matchedProduct.qty ? matchedProduct.qty : 1;
75
+ } catch (err) {
76
+ console.log("Error while building payload");
77
+ }
78
+
79
+ return payload;
80
+ };
81
+
82
+ const _equinoxRequest = async options => {
83
+ const {
84
+ marketLocale,
85
+ storeId,
86
+ storefrontURL,
87
+ headers
88
+ } = await getEquinoxRequestConfiguration();
89
+ const equinoxParams = {
90
+ locale: marketLocale,
91
+ storeId: storeId
92
+ };
93
+
94
+ let axiosRequest = {
95
+ method: options.method,
96
+ url: `${storefrontURL}/orchestrationservices/storefront/${options.endpoint}`,
97
+ headers: headers,
98
+ withCredentials: options.method !== "DELETE",
99
+ params: equinoxParams
100
+ };
101
+
102
+ if (options.data) {
103
+ axiosRequest.data = options.data;
104
+ }
105
+
106
+ return axios(axiosRequest);
107
+ };
108
+
109
+ const getEquinoxRequestConfiguration = async () => {
110
+ const marketLocale = UrlService.getLocale();
111
+ const config = (await getConfiguration(["Equinox_Markets"])).Equinox_Markets;
112
+ const storefrontURL = config.API_Base_URLs;
113
+
114
+ const sessionData = sessionStorage.getItem("oktaTokens");
115
+ const parsedSessionData = JSON.parse(sessionData);
116
+ const sessionOktaToken =
117
+ parsedSessionData && parsedSessionData.accessToken
118
+ ? parsedSessionData.accessToken
119
+ : "";
120
+ const localStorageOktaToken = localStorage.getItem("equinox-okta-token");
121
+
122
+ let oktaToken = localStorageOktaToken
123
+ ? localStorageOktaToken
124
+ : sessionOktaToken;
125
+
126
+ const headers = {
127
+ Accept: "*",
128
+ "Content-Type": "application/json",
129
+ Authorization: `Bearer ${oktaToken}`
130
+ };
131
+
132
+ return {
133
+ marketLocale,
134
+ storeId: config.store_id,
135
+ storefrontURL,
136
+ headers
137
+ };
138
+ };
@@ -200,40 +200,11 @@
200
200
  </template>
201
201
 
202
202
  <script>
203
- import {
204
- QualificationService as OldQualificationService,
205
- CartService
206
- } from "@nuskin/ns-shop";
207
- import { getConfiguration } from "@nuskin/configuration-sdk";
208
- import { default as NewQualificationService } from "@nuskin/exclusive-offer-sdk";
203
+ import { CartService } from "@nuskin/ns-shop";
204
+ import QualificationService from "@nuskin/exclusive-offer-sdk";
209
205
  import NsProductMixin from "../mixins/NsProductMixin";
210
206
  import "@nuskin/ns-core-styles/src/dist/main.css";
211
207
 
212
- let equinoxMarket = {
213
- promise: getConfiguration(["Equinox_Markets"]).then(config => {
214
- equinoxMarket = {
215
- value:
216
- config && config.Equinox_Markets
217
- ? !!config.Equinox_Markets.active
218
- : false,
219
- promise: null,
220
- loaded: true
221
- };
222
- }),
223
- loaded: false,
224
- value: undefined
225
- };
226
-
227
- const getQualificationService = async () => {
228
- if (!equinoxMarket.loaded) {
229
- await equinoxMarket.promise;
230
- }
231
- if (equinoxMarket.value) {
232
- return NewQualificationService;
233
- }
234
- return OldQualificationService;
235
- };
236
-
237
208
  export default {
238
209
  name: "NsProductMixinPreview",
239
210
  filters: {
@@ -407,9 +378,9 @@ export default {
407
378
  async mounted() {
408
379
  if (this.isExclusive) {
409
380
  if (this.isLoggedIn) {
410
- this.productQualification = await (
411
- await getQualificationService()
412
- ).getQualification(this.activeSku);
381
+ this.productQualification = await QualificationService.getQualification(
382
+ this.activeSku
383
+ );
413
384
 
414
385
  this.cartItem = await CartService.getFirstItemBySku(this.activeSku, {
415
386
  cartOrderItems: true