@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.
@@ -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
- }