@nuskin/product-components 3.18.0-td-341.2 → 4.0.0-cx15-11969.1
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/.releaserc +1 -1
- package/components/NsProductCarousel.vue +0 -38
- package/components/NsProductList.vue +4 -7
- package/docs/CHANGELOG.md +1 -1
- package/index.js +1 -5
- package/mixins/NsProductMixin.js +7 -34
- package/package.json +4 -8
- package/services/NsProductAppService.js +6 -37
- package/services/NsProductDataService.js +4 -5
- package/assets/imageplaceholder.svg +0 -39
- package/components/NsProductRecsCarousel.vue +0 -155
- package/components/NuHomeCloneProductCarousel/Carousel.vue +0 -209
- package/components/NuHomeCloneProductCarousel/CarouselChevron.vue +0 -93
- package/components/NuHomeCloneProductCarousel/Loader.vue +0 -72
- package/components/NuHomeCloneProductCarousel/NuHomeCloneProductCarousel.vue +0 -1100
- package/components/NuHomeCloneProductCarousel/Price.vue +0 -147
- package/gl-sbom-npm-yarn.cdx.json +0 -5946
- package/mixins/globalHelper.js +0 -66
- package/models/product.js +0 -140
- package/services/NsProductContentService.js +0 -147
- package/services/NsProductRecommendationClient.js +0 -66
package/mixins/globalHelper.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const MPP_LOCALE_EXCEPTIONS = ["en", "US-es", "US-zh", "US-vi"];
|
|
2
|
-
|
|
3
|
-
export default {
|
|
4
|
-
methods: {
|
|
5
|
-
getMarketAndLanguage() {
|
|
6
|
-
let market = "US";
|
|
7
|
-
let language = "en";
|
|
8
|
-
|
|
9
|
-
const aemLocalePattern = /[a-z]{2}_[A-Z]{2}/;
|
|
10
|
-
const localeArray = window.location.href.match(aemLocalePattern);
|
|
11
|
-
|
|
12
|
-
if (localeArray) {
|
|
13
|
-
const locale = localeArray[0].split("_");
|
|
14
|
-
|
|
15
|
-
if (!MPP_LOCALE_EXCEPTIONS.includes([locale[1], locale[0]].join("-"))) {
|
|
16
|
-
language = locale[0].toLowerCase().trim();
|
|
17
|
-
market = locale[1].toLowerCase().trim();
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
market,
|
|
23
|
-
language
|
|
24
|
-
};
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
buildProductLink(slug, sku) {
|
|
28
|
-
const { market, language } = this.getMarketAndLanguage();
|
|
29
|
-
const isNewPdp =
|
|
30
|
-
typeof window.isNewPdp !== "undefined" ? window.isNewPdp : false;
|
|
31
|
-
|
|
32
|
-
let pdpLink = `/content/nuskin/${language}_${market.toUpperCase()}/products/product.${sku}.html`;
|
|
33
|
-
|
|
34
|
-
if (isNewPdp) {
|
|
35
|
-
pdpLink = `/catalog/${market}/${language}/product/${slug}`;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return pdpLink;
|
|
39
|
-
},
|
|
40
|
-
currencyFormatter(currencyLocale, style, currency, price) {
|
|
41
|
-
const resolvedCurrencyLocale = this.resolveCurrencyLocale(currencyLocale);
|
|
42
|
-
const formatter = new Intl.NumberFormat(resolvedCurrencyLocale, {
|
|
43
|
-
maximumFractionDigits: 2,
|
|
44
|
-
currency: currency,
|
|
45
|
-
style: style
|
|
46
|
-
});
|
|
47
|
-
return formatter.format(price);
|
|
48
|
-
},
|
|
49
|
-
resolveCurrencyLocale(currencyLocale) {
|
|
50
|
-
const localeArray = currencyLocale.split("-");
|
|
51
|
-
let language = localeArray[0];
|
|
52
|
-
const market = localeArray[1];
|
|
53
|
-
|
|
54
|
-
/*
|
|
55
|
-
CX32-2339: Product currency for en_CZ locale should be Kč instead of CZK.
|
|
56
|
-
We'll change the language from en to cs in order to change the currency symbol to Kč.
|
|
57
|
-
*/
|
|
58
|
-
if (language === "en" && market === "CZ") {
|
|
59
|
-
language = "cs";
|
|
60
|
-
return `${language}-${market}`;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return currencyLocale;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
package/models/product.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import placeholder from "../assets/imageplaceholder.svg";
|
|
2
|
-
|
|
3
|
-
const productStatus = {
|
|
4
|
-
NS_PRODUCT_RELEASED_FOR_SALE: "RELEASED_FOR_SALE",
|
|
5
|
-
NS_PRODUCT_NOT_RELEASED_FOR_SALE: "NOT_RELEASED_FOR_SALE",
|
|
6
|
-
NS_PRODUCT_STOP_STATUS: "STOP_STATUS",
|
|
7
|
-
NS_PRODUCT_NOT_SOLD_SEPARATELY: "NOT_SOLD_SEPERATELY",
|
|
8
|
-
NS_PRODUCT_DISCONTINUED: "DISCONTINUED",
|
|
9
|
-
NS_PRODUCT_NOT_FOUND: "NOT_FOUND"
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Model object for product
|
|
14
|
-
*/
|
|
15
|
-
export default class Product {
|
|
16
|
-
/**
|
|
17
|
-
* Copnstructor
|
|
18
|
-
* @param {string} id
|
|
19
|
-
* @param {json} productData product data object
|
|
20
|
-
*/
|
|
21
|
-
constructor(id, productData) {
|
|
22
|
-
this._id = id;
|
|
23
|
-
Object.assign(this, productData);
|
|
24
|
-
this.initProductImage();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Init function
|
|
29
|
-
* @return {void}
|
|
30
|
-
*/
|
|
31
|
-
initProductImage() {
|
|
32
|
-
let image;
|
|
33
|
-
const variantWithImage = this.getFirstVariantWithImage();
|
|
34
|
-
if (this.hasVariants() && variantWithImage) {
|
|
35
|
-
image = variantWithImage.productImages[0];
|
|
36
|
-
} else if (this.hasProductImage()) {
|
|
37
|
-
image = this.productImages[0];
|
|
38
|
-
} else {
|
|
39
|
-
image = { url: placeholder, alt: "placeholder" };
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
this.carouselImage = image;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Gets the first variant with an image
|
|
47
|
-
* @return {any} Variant object
|
|
48
|
-
*/
|
|
49
|
-
getFirstVariantWithImage() {
|
|
50
|
-
// First, attempt to find a variant that matches this product's internal ID
|
|
51
|
-
const matchingVariant = this.variants.find(
|
|
52
|
-
variant =>
|
|
53
|
-
variant.sku === this._id &&
|
|
54
|
-
variant.productImages &&
|
|
55
|
-
variant.productImages.length
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
if (matchingVariant) return matchingVariant;
|
|
59
|
-
|
|
60
|
-
return this.variants.find(
|
|
61
|
-
variant => variant.productImages && variant.productImages.length
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
get fullTitle() {
|
|
66
|
-
const baseSegment = this.title;
|
|
67
|
-
const variant = this.firstVariant;
|
|
68
|
-
|
|
69
|
-
let variantSegment = "";
|
|
70
|
-
|
|
71
|
-
if (variant && variant.variantLabel) {
|
|
72
|
-
variantSegment = " – " + variant.variantLabel;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return baseSegment + variantSegment;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Getter for the first product variant
|
|
80
|
-
*/
|
|
81
|
-
get firstVariant() {
|
|
82
|
-
// First, attempt to find a variant that matches this product's internal ID
|
|
83
|
-
const matchingVariant = this.variants.find(v => v.sku === this._id);
|
|
84
|
-
|
|
85
|
-
if (matchingVariant) return matchingVariant;
|
|
86
|
-
|
|
87
|
-
return this.variants.length > 0 && this.variants[0];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Getter for the first product variant's primaryBrand
|
|
92
|
-
*/
|
|
93
|
-
get primaryBrand() {
|
|
94
|
-
return this.firstVariant && this.firstVariant.primaryBrand;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Checks if product has a variant
|
|
99
|
-
* @return {boolean}
|
|
100
|
-
*/
|
|
101
|
-
hasVariants() {
|
|
102
|
-
return this.variants && this.variants.length > 0;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Checks if product has an image
|
|
107
|
-
* @return {boolean}
|
|
108
|
-
*/
|
|
109
|
-
hasProductImage() {
|
|
110
|
-
return this.productImages && this.productImages.length > 0;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Checks if product is not available for sale
|
|
115
|
-
* @return {boolean}
|
|
116
|
-
*/
|
|
117
|
-
isNotReleasedForSale() {
|
|
118
|
-
return (
|
|
119
|
-
this.hasVariants() &&
|
|
120
|
-
this.firstVariant.status.retail ==
|
|
121
|
-
productStatus.NS_PRODUCT_NOT_RELEASED_FOR_SALE
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Checks if product is exclusive
|
|
127
|
-
* @return {boolean}
|
|
128
|
-
*/
|
|
129
|
-
isExclusive() {
|
|
130
|
-
return this.hasVariants() && this.firstVariant.isExclusive;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Checks if product is out of stock
|
|
135
|
-
* @return {boolean}
|
|
136
|
-
*/
|
|
137
|
-
isOutOfStock() {
|
|
138
|
-
return this.hasVariants() && this.firstVariant.availableQuantity < 1;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
import { RunConfigService } from "@nuskin/ns-util";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @typedef ProductData
|
|
6
|
-
* @property {string} id
|
|
7
|
-
* @property {string} title
|
|
8
|
-
* @property {object[]} [productImages]
|
|
9
|
-
* @property {string} [productImages.url]
|
|
10
|
-
* @property {string} [productImages.alt]
|
|
11
|
-
* @property {string} [slug]
|
|
12
|
-
* @property {string} [salesLabel]
|
|
13
|
-
* @property {object[]} [variants]
|
|
14
|
-
* @property {string} [variants.sku]
|
|
15
|
-
* @property {string} [variants.variantLabel]
|
|
16
|
-
* @property {boolean} [variants.isExclusive]
|
|
17
|
-
* @property {number} [variants.availableQuantity]
|
|
18
|
-
* @property {object[]} [variants.productImages]
|
|
19
|
-
* @property {string} [variants.productImages.url]
|
|
20
|
-
* @property {string} [variants.productImages.alt]
|
|
21
|
-
* @property {string} [variants.pricingJson]
|
|
22
|
-
* @property {object} [variants.price]
|
|
23
|
-
* @property {string} [variants.price.currencyCode]
|
|
24
|
-
* @property {number} [variants.price.retail]
|
|
25
|
-
* @property {number} [variants.price.retailSales]
|
|
26
|
-
* @property {number} [variants.price.retailSubscription]
|
|
27
|
-
* @property {number} [variants.price.wholesale]
|
|
28
|
-
* @property {number} [variants.price.wholesaleSales]
|
|
29
|
-
* @property {number} [variants.price.wholesaleSubscription]
|
|
30
|
-
* @property {object} [status]
|
|
31
|
-
* @property {'RELEASED_FOR_SALE'|'NOT_RELEASED_FOR_SALE'|'DISCONTINUED'|'STOP_STATUS'|'NOT_FOUND'|'NOT_SOLD_SEPERATELY'|'inactive'|'sellable'|'preview'|'discontinued'|'stopped'|'replacement'|'bundleOnly'|'test'} [status.status]
|
|
32
|
-
* @property {boolean} [status.isBackordered]
|
|
33
|
-
* @property {string} [status.backorderedAvailableDate]
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
const ProductsByIdQuery = `
|
|
37
|
-
query ProductsById($ids: [String!]!, $market: String, $language: String) {
|
|
38
|
-
productsById(ids: $ids, market: $market, language: $language, quantity: 1) {
|
|
39
|
-
products {
|
|
40
|
-
id
|
|
41
|
-
title
|
|
42
|
-
productImages {
|
|
43
|
-
url
|
|
44
|
-
alt
|
|
45
|
-
}
|
|
46
|
-
slug
|
|
47
|
-
salesLabel
|
|
48
|
-
variants {
|
|
49
|
-
sku
|
|
50
|
-
variantLabel
|
|
51
|
-
isExclusive
|
|
52
|
-
availableQuantity
|
|
53
|
-
productImages {
|
|
54
|
-
url
|
|
55
|
-
alt
|
|
56
|
-
}
|
|
57
|
-
pricingJson
|
|
58
|
-
price {
|
|
59
|
-
currencyCode
|
|
60
|
-
retail
|
|
61
|
-
retailSales
|
|
62
|
-
retailSubscription
|
|
63
|
-
wholesale
|
|
64
|
-
wholesaleSales
|
|
65
|
-
wholesaleSubscription
|
|
66
|
-
}
|
|
67
|
-
status {
|
|
68
|
-
status
|
|
69
|
-
isBackordered
|
|
70
|
-
backorderedAvailableDate
|
|
71
|
-
}
|
|
72
|
-
primaryBrand
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
`;
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* @param {string[]} fields
|
|
81
|
-
* @returns {string}
|
|
82
|
-
*/
|
|
83
|
-
const productTranslationsQuery = fields => `
|
|
84
|
-
query ProductTranslationsQuery($market: String, $language: String) {
|
|
85
|
-
pdpStrings(market: $market, language: $language) {
|
|
86
|
-
${fields.join("\n")}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
`;
|
|
90
|
-
|
|
91
|
-
function getProductGraphqlUrl() {
|
|
92
|
-
/** @type {'dev'|'test'|'stage'|'prod'|'unknown'} */
|
|
93
|
-
const env = RunConfigService.getEnvironmentCode();
|
|
94
|
-
|
|
95
|
-
switch (env) {
|
|
96
|
-
case "dev":
|
|
97
|
-
return "https://product.api.dev.nuskin.com/graphql";
|
|
98
|
-
case "stage":
|
|
99
|
-
case "test":
|
|
100
|
-
return "https://product.api.test.nuskin.com/graphql";
|
|
101
|
-
case "unknown":
|
|
102
|
-
case "prod":
|
|
103
|
-
default:
|
|
104
|
-
return "https://product.api.nuskin.com/graphql";
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Retrieves product data for the given SKUs and/or product UIDs
|
|
110
|
-
* @param {string[]} ids Product IDs; can be SKUs and/or UIDs
|
|
111
|
-
* @param {string} market Market country code
|
|
112
|
-
* @param {string} language Market language code
|
|
113
|
-
* @return {Promise<ProductData[]>}
|
|
114
|
-
*/
|
|
115
|
-
export async function findProductsById(ids, market, language) {
|
|
116
|
-
const url = getProductGraphqlUrl();
|
|
117
|
-
|
|
118
|
-
const response = await axios.post(url, {
|
|
119
|
-
query: ProductsByIdQuery,
|
|
120
|
-
variables: { ids, market, language }
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
if (!response.data || !response.data.data) return [];
|
|
124
|
-
|
|
125
|
-
return response.data.data.productsById.products;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* @param {string[]} fields Product string fields to get
|
|
130
|
-
* @param {string} market Market country code
|
|
131
|
-
* @param {string} language Market language code
|
|
132
|
-
*/
|
|
133
|
-
export async function getProductTranslations(fields, market, language) {
|
|
134
|
-
// If no fields are specified, don't bother attempting an API query
|
|
135
|
-
if (fields.length === 0) return {};
|
|
136
|
-
|
|
137
|
-
const url = getProductGraphqlUrl();
|
|
138
|
-
const query = productTranslationsQuery(fields);
|
|
139
|
-
|
|
140
|
-
const response = await axios.post(url, {
|
|
141
|
-
query,
|
|
142
|
-
variables: { market, language }
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
if (!response.data || !response.data.data) return {};
|
|
146
|
-
return response.data.data.pdpStrings || {};
|
|
147
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { UserService } from "@nuskin/ns-account";
|
|
2
|
-
import { RunConfigService } from "@nuskin/ns-util";
|
|
3
|
-
import axios from "axios";
|
|
4
|
-
|
|
5
|
-
function getApiBaseUrl() {
|
|
6
|
-
/** @type {"dev"|"test"|"stage"|"prod"|"unknown"} */
|
|
7
|
-
const env = RunConfigService.getEnvironmentCode();
|
|
8
|
-
|
|
9
|
-
switch (env) {
|
|
10
|
-
case "prod":
|
|
11
|
-
return "https://product-recommendations.api.nuskin.com";
|
|
12
|
-
case "test":
|
|
13
|
-
return "https://product-recommendations.api.test.nuskin.com";
|
|
14
|
-
case "unknown":
|
|
15
|
-
case "dev":
|
|
16
|
-
default:
|
|
17
|
-
return "https://product-recommendations.api.dev.nuskin.com";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
*
|
|
23
|
-
* @param {number} [limit] Maximum number of recommended products to return
|
|
24
|
-
* @return {Promise<string[]>} List of SKU IDs of recommended products
|
|
25
|
-
*/
|
|
26
|
-
export async function getPersonalizedRecommendations(limit) {
|
|
27
|
-
// Get user ID
|
|
28
|
-
const user = UserService.getUser();
|
|
29
|
-
if (!user) return [];
|
|
30
|
-
const userId = user.id;
|
|
31
|
-
|
|
32
|
-
// Get okta access token
|
|
33
|
-
const tokensString = sessionStorage.getItem("oktaTokens");
|
|
34
|
-
if (!tokensString) return [];
|
|
35
|
-
const { accessToken } = JSON.parse(tokensString);
|
|
36
|
-
|
|
37
|
-
const baseUrl = getApiBaseUrl();
|
|
38
|
-
const url = new URL(`/v1/personalized-for/${userId}`, baseUrl).toString();
|
|
39
|
-
|
|
40
|
-
const { data } = await axios.get(url, {
|
|
41
|
-
headers: { Authorization: `Bearer ${accessToken}` },
|
|
42
|
-
params: { limit }
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
return data.recommendedSkus || [];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
50
|
-
* @param {string} sku Product SKU ID
|
|
51
|
-
* @param {number} [limit] Maximum number of recommended products to return
|
|
52
|
-
* @return {Promise<string[]>} List of SKU IDs of recommended products
|
|
53
|
-
*/
|
|
54
|
-
export async function getRelatedProducts(sku, limit) {
|
|
55
|
-
// // If no SKU ID is given, don't attempt to retrieve related products
|
|
56
|
-
if (!sku) return [];
|
|
57
|
-
|
|
58
|
-
const baseUrl = getApiBaseUrl();
|
|
59
|
-
const url = new URL(`/v1/related-to/${sku}`, baseUrl).toString();
|
|
60
|
-
|
|
61
|
-
const { data } = await axios.get(url, {
|
|
62
|
-
params: { limit }
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return data.recommendedSkus || [];
|
|
66
|
-
}
|