@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
|
-
|
|
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:
|
|
4
|
+
"serialNumber": "urn:uuid:d791f546-6b5a-47e2-b278-d5d586e7caee",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
|
-
"timestamp": "2024-06-
|
|
7
|
+
"timestamp": "2024-06-17T16:53:01Z",
|
|
8
8
|
"tools": [
|
|
9
9
|
{
|
|
10
10
|
"vendor": "GitLab",
|
package/mixins/NsProductMixin.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
-
|
|
732
|
-
)
|
|
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
|
-
|
|
764
|
-
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
412
|
-
)
|
|
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
|