@nuskin/ns-shop 7.0.11-pur-813.1 → 7.0.11-pur-813.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/ns-shop",
3
- "version": "7.0.11-pur-813.1",
3
+ "version": "7.0.11-pur-813.2",
4
4
  "description": "The description that will amaze and astound your audience when they read it",
5
5
  "main": "src/shop.js",
6
6
  "scripts": {
@@ -26,7 +26,7 @@
26
26
  "@nuskin/ns-common-lib": "1.4.7",
27
27
  "@nuskin/ns-feature-flags": "1.4.7",
28
28
  "@nuskin/ns-loyalty-web": "1.5.6",
29
- "@nuskin/ns-product-lib": "2.17.6",
29
+ "@nuskin/ns-product-lib": "2.19.1-pur-816.1",
30
30
  "@nuskin/nuskinjquery": "2.3.1",
31
31
  "@nuskin/order-model": "3.1.3",
32
32
  "@nuskin/product-lib": "2.2.1",
@@ -0,0 +1,318 @@
1
+ import {productLib} from '@nuskin/product-lib';
2
+ import {RunConfigService, events, UrlService, BrowserDetection} from '@nuskin/ns-util';
3
+ import { getConfiguration } from '@nuskin/configuration-sdk';
4
+ import {AccountManager} from '@nuskin/ns-account';
5
+ import {PriceType, Product} from '@nuskin/ns-product-lib';
6
+
7
+ const extractProductData = (data, sku) => {
8
+ let retVal;
9
+
10
+ if (Array.isArray(data.variants) && data.variants.length > 0) {
11
+ retVal = data.variants.find((variant) => variant.sku === sku);
12
+ } else if (data.bundle) {
13
+ retVal = data.bundle;
14
+ retVal.title = data.title;
15
+ retVal.productImages = data.productImages;
16
+ }
17
+
18
+ return retVal;
19
+ }
20
+
21
+ const addPrice = (productData, priceTypeKey, subKey, priceValue) => {
22
+ if (!['Points', 'cv', 'price', 'pv'].includes(priceTypeKey)) {
23
+ // if the priceTypeKey is not one of these, then we don't want it to show.
24
+ return;
25
+ }
26
+ if (priceTypeKey === 'pv') {
27
+ priceTypeKey = 'psv';
28
+ }
29
+ if (priceTypeKey === 'cv') {
30
+ priceTypeKey = 'csv';
31
+ }
32
+
33
+ if (productData[priceTypeKey] === undefined) {
34
+ if (priceTypeKey === 'Points') {
35
+ if (!productData.price) {
36
+ productData.price = {};
37
+ }
38
+ productData.price.PTS = priceValue;
39
+ return;
40
+ } else {
41
+ productData[priceTypeKey] = {};
42
+ }
43
+ }
44
+ productData[priceTypeKey][subKey] = priceValue;
45
+ }
46
+
47
+ const convertPricing = (pricing, productData) => {
48
+ if (pricing) {
49
+ Object.keys(pricing).forEach((channelKey) => {
50
+ Object.keys(pricing[channelKey]).forEach((contextKey) => {
51
+ Object.keys(pricing[channelKey][contextKey]).forEach((priceTypeKey) => {
52
+ const priceValue = pricing[channelKey][contextKey][priceTypeKey];
53
+ switch (channelKey) {
54
+ case 'wholesale':
55
+ switch (contextKey) {
56
+ case 'webOrder':
57
+ addPrice(productData, priceTypeKey, 'WWHL', priceValue);
58
+ break;
59
+ case 'webAdr':
60
+ case 'WADW':
61
+ case 'ADR5':
62
+ addPrice(productData, priceTypeKey, 'WADW', priceValue);
63
+ break;
64
+ case 'order':
65
+ case 'WHL':
66
+ addPrice(productData, priceTypeKey, 'WHL', priceValue);
67
+ break;
68
+ case 'adr':
69
+ addPrice(productData, priceTypeKey, 'ADW', priceValue);
70
+ break;
71
+ case 'ADR10':
72
+ case 'WADWD':
73
+ addPrice(productData, priceTypeKey, 'WADWD', priceValue);
74
+ break;
75
+ default:
76
+ addPrice(
77
+ productData,
78
+ priceTypeKey,
79
+ `${contextKey}-WWHL`,
80
+ priceValue
81
+ );
82
+ break;
83
+ }
84
+ break;
85
+ case 'retail':
86
+ switch (contextKey) {
87
+ case 'webOrder':
88
+ addPrice(productData, priceTypeKey, 'WRTL', priceValue);
89
+ break;
90
+ case 'webAdr':
91
+ addPrice(productData, priceTypeKey, 'WADR', priceValue);
92
+ break;
93
+ case 'order':
94
+ case 'RTL':
95
+ addPrice(productData, priceTypeKey, 'RTL', priceValue);
96
+ break;
97
+ case 'adr':
98
+ addPrice(productData, priceTypeKey, 'ADR', priceValue);
99
+ break;
100
+ default:
101
+ addPrice(
102
+ productData,
103
+ priceTypeKey,
104
+ `${contextKey}-WRTL`,
105
+ priceValue
106
+ );
107
+ break;
108
+ }
109
+ break;
110
+ }
111
+ });
112
+ });
113
+ });
114
+ }
115
+ }
116
+
117
+ const createDataFromSkuSearch = (searchData, options) => {
118
+ if (!searchData) {
119
+ return new Product();
120
+ }
121
+ let product = new Product(),
122
+ langObj = null, //getLangObj(searchData['contents']['language'], options.language),
123
+ marketObj = null; //searchData['market'];
124
+ // if (Array.isArray(marketObj)) {
125
+ // marketObj = getMarketObj(searchData['market'], options.country);
126
+ // }
127
+ let {orderType: mktOrderTypes, custType: mktCustTypes, restrictedMarket: restrictedMarkets, addOns} = {}; //marketObj;
128
+
129
+ if (searchData) {
130
+ product.sku = options.sku;
131
+ product.lang = options.language;
132
+ product.isExclusive = searchData.isExclusive;
133
+ if (Array.isArray(searchData.productImages) && searchData.productImages.length > 0) {
134
+ product.setFullImage(searchData.productImages[0].url);
135
+ product.setThumbnail(searchData.productImages[0].thumbnail);
136
+ // product.setProductCarouselImages(???searchData.contents.imageCarousel);
137
+ }
138
+ // product.productLabels = ???
139
+ product.title = searchData.title;
140
+ product.shortDescr = searchData.description;
141
+
142
+ const pricing = JSON.parse(searchData.pricingJson);
143
+ const orderTypes = [];
144
+ if (searchData.purchaseTypes.buyOnce) orderTypes.push('order');
145
+ if (searchData.purchaseTypes.subscription) orderTypes.push('adr');
146
+ let status = {
147
+ sku: options.sku,
148
+ globalProductId: searchData.globalId,
149
+ status: searchData.status.status === 'sellable' ? 'RELEASED_FOR_SALE' : 'NOT_RELEASED_FOR_SALE',
150
+ availableQuantity: searchData.availableQuantity,
151
+ maxQuantity: searchData.maxQuantity,
152
+ locallyProduced: searchData.locallyProduced, // ???
153
+ backorderedAvailableDate: searchData.status.isBackordered > 0 ? searchData.status.backorderedAvailableDate : null,
154
+ orderType: orderTypes, // searchData.channels.map((channel) => channel.toLowerCase()),
155
+ childSkus: null // ???
156
+ };
157
+ convertPricing(pricing, status);
158
+ product.addPricingFromStatus(status);
159
+ }
160
+
161
+
162
+ if (marketObj !== null) {
163
+ // product.sku = searchData.sku;
164
+ // product.isExclusive = Object.hasOwn(searchData, 'isExclusive') ? searchData.isExclusive : false;
165
+ if (Object.hasOwn(searchData, 'contents')) {
166
+ // product.setFullImage(searchData.contents.fullImage);
167
+ // product.setThumbnail(searchData.contents.fullImage.replace(/\.img\.[0-9]+\.[0-9]+\.png/i, '.img.160.160.png'));
168
+ // if (Object.hasOwn(searchData.contents, 'imageCarousel')) {
169
+ // product.setProductCarouselImages(searchData.contents.imageCarousel);
170
+ // }
171
+ }
172
+ // AEM-8320 - it returns two BP (08007070 and 08129080), 08129080 does not have any translation yet.
173
+ // Note that langObj can be null (have a BP that does not have any translation)
174
+
175
+ // product.productLabels = marketObj['populateProductLabels'];
176
+ if (langObj) {
177
+ // product.lang = langObj['languageCode'];
178
+ // product.title = langObj['name'];
179
+ // product.shortDescr = langObj['shortDescription'];
180
+ product.longDescr = langObj['longDescription'];
181
+ product.ingredients = langObj['ingredients'];
182
+ product.benefits = langObj['benefits'];
183
+ product.usage = langObj['usage'];
184
+ product.resources = langObj['resources'];
185
+ product.videos = langObj['videos'];
186
+ product.contentSection = langObj['contentSection'];
187
+ product.sizeWeight = langObj['size_weight'];
188
+ product.nettoWeight = langObj['netto_weight'];
189
+ product.salesLabel = langObj['salesLabel'];
190
+ product.movie = langObj['movie'];
191
+ product.youtube = langObj['youtube'];
192
+ product.salesEventText = langObj['tdc_salesEventText'];
193
+ if (langObj.variantInfo) {
194
+ if (langObj.variantInfo.tdc_dropdownLabel) {
195
+ product.variantDropdownLabel = langObj.variantInfo.tdc_dropdownLabel
196
+ }
197
+ if (langObj.variantInfo.tdc_dropdownPlaceholder) {
198
+ product.variantDropdownPlaceholder = langObj.variantInfo.tdc_dropdownPlaceholder;
199
+ }
200
+ if (langObj.variantInfo.tdc_variantsLabel) {
201
+ product.variantsLabel = langObj.variantInfo.tdc_variantsLabel;
202
+ }
203
+ }
204
+ }
205
+ product.setImageAltText(searchData['contents']['altText']);
206
+ product.country = marketObj['countryCode'];
207
+ product.scanQualified = marketObj['scanQualified'];
208
+ product.division = searchData['division'];
209
+
210
+ if (mktOrderTypes) {
211
+ product.orderTypes = mktOrderTypes;
212
+ }
213
+ if (mktCustTypes) {
214
+ product.custTypes = mktCustTypes;
215
+ }
216
+ if (restrictedMarkets) {
217
+ product.restrictedMarkets = restrictedMarkets;
218
+ }
219
+ if (addOns) {
220
+ product.addOns = addOns;
221
+ } else {
222
+ product.addOns = [];
223
+ }
224
+
225
+ if (options.isGroupOffer)
226
+ product.isGroupOffer = options.isGroupOffer;
227
+ if (options.isPersonalOffer)
228
+ product.isPersonalOffer = options.isPersonalOffer;
229
+ }
230
+
231
+ if (searchData.variantConfig) {
232
+ if (searchData.variantConfig.variantType) {
233
+ product.variantType = searchData.variantConfig.variantType;
234
+ }
235
+ if (searchData.variantConfig.variants) {
236
+ searchData.variantConfig.variants.forEach((variant) => {
237
+ product.setVariant(createDataFromSkuSearch(variant, options));
238
+ });
239
+ }
240
+ if (searchData.variantConfig.swatchColor) {
241
+ product.shade = searchData.variantConfig.swatchColor;
242
+ }
243
+ if (searchData.variantConfig.baseSku) {
244
+ product.baseSku = searchData.variantConfig.baseSku;
245
+ }
246
+ }
247
+
248
+ return product;
249
+ }
250
+
251
+ const handleDetailResponse = async (responseData, options) => {
252
+ let retVal = {success: true},
253
+ dataForProduct = createDataFromSkuSearch(responseData, options);
254
+
255
+ if (dataForProduct.sku) {
256
+ retVal.product = new Product(dataForProduct);
257
+
258
+ const siteUrl = UrlService.getSiteUrl();
259
+ const isEdge = BrowserDetection.isEdge();
260
+ const isFirefox = BrowserDetection.isFirefox();
261
+
262
+ // set the base url for images, etc
263
+ retVal.product.setBaseUrl(siteUrl);
264
+
265
+ // fix image paths for non-edge/firefox
266
+ if (!isEdge && !isFirefox) {
267
+ retVal.product.fullImage = retVal.product.fullImage
268
+ ? `${retVal.product.fullImage.split("?")[0]}?format=pjpg`
269
+ : undefined;
270
+ retVal.product.thumbnail = retVal.product.thumbnail
271
+ ? `${retVal.product.thumbnail.split("?")[0]}?format=pjpg`
272
+ : undefined;
273
+ }
274
+ } else {
275
+ retVal.success = false;
276
+ retVal.failedSku = options.sku;
277
+ }
278
+
279
+ if (retVal.product && options.priceType) {
280
+ const {Cart: cartCfg} = await getConfiguration(['Cart']);
281
+ const priceType = cartCfg.showWholeSalePricing && !AccountManager.isLoggedIn()
282
+ ? PriceType.WWHL
283
+ : options.priceType;
284
+ retVal.product.setPriceAndPvFromType(priceType);
285
+ }
286
+
287
+ return retVal;
288
+ }
289
+
290
+ const getProductDetail = async (options) => {
291
+ let response;
292
+
293
+ // Hit the PMD and get all the data for the product including the price information.
294
+ try {
295
+ response = await productLib.getProduct({
296
+ fromId: options.sku,
297
+ env: RunConfigService.getEnvironmentCode(),
298
+ market: options.country,
299
+ language: options.language
300
+ })
301
+ } catch (e) {
302
+ throw {error: e, failedSku: options.sku, type: events.errors.PRODUCT_LOOKUP};
303
+ }
304
+
305
+ return extractProductData(response.data, options.sku);
306
+ }
307
+
308
+ const getProductBySku = async (options) => {
309
+ const data = await getProductDetail(options);
310
+
311
+ let result = await handleDetailResponse(data, options);
312
+
313
+ return result;
314
+ }
315
+
316
+ export {
317
+ getProductBySku
318
+ }
@@ -1,11 +1,12 @@
1
1
  import $ from '@nuskin/nuskinjquery';
2
2
  import {PriceType, Product} from '@nuskin/ns-product-lib';
3
- import {RunConfigService, events, BrowserDetection, UrlService} from '@nuskin/ns-util';
4
- import {AccountManager} from '@nuskin/ns-account';
3
+ import {RunConfigService, events, util, BrowserDetection, UrlService} from '@nuskin/ns-util';
4
+ import {UserService, AccountManager} from '@nuskin/ns-account';
5
5
  import ProductStatusService from '../product/ProductStatusService.js';
6
6
  import _ from 'lodash';
7
7
  import { getConfiguration } from '@nuskin/configuration-sdk';
8
- import {productLib} from '@nuskin/product-lib';
8
+ import { getProductBySku as getCsProductBySku } from './csProductHelper.js';
9
+ import axios from 'axios';
9
10
 
10
11
  let ProductService = function() {
11
12
  // ---------------------------------------------
@@ -21,24 +22,13 @@ let ProductService = function() {
21
22
  * @returns {null}
22
23
  */
23
24
  getProductBySku: async function(_options) {
24
- let options = verifyLocaleFields(_options),
25
- promises = [getProductDetail(options)];
26
-
27
- promises.push(getProductStatus(options));
28
-
29
- let values = await Promise.all(promises);
30
-
31
- let result = await handleDetailResponse(values[0], options);
32
-
33
- if (values[1] && values[1][0]) {
34
- handleStatusResponse(result.product, values[1][0], options);
25
+ const options = verifyLocaleFields(_options)
26
+ const {Cart: cartCfg} = await getConfiguration(['Cart']);
27
+ if (cartCfg.useContentStackProductData) {
28
+ return await getCsProductBySku(options)
35
29
  } else {
36
- // Fallback for product quantity on a failed product status call.
37
- // This is done to prevent the product detail component from locking up unnecessarily.
38
- result.product.availableQuantity = 10000;
30
+ return await legacyGetProductBySku(options);
39
31
  }
40
-
41
- return result;
42
32
  },
43
33
 
44
34
  /**
@@ -84,22 +74,42 @@ let ProductService = function() {
84
74
  // Private Methods
85
75
  //
86
76
  // ---------------------------------------------
77
+ async function legacyGetProductBySku(options) {
78
+ const promises = [getProductDetail(options)];
79
+
80
+ promises.push(getProductStatus(options));
81
+
82
+ let values = await Promise.all(promises);
83
+
84
+ let result = await handleDetailResponse(values[0], options);
85
+
86
+ if (values[1] && values[1][0]) {
87
+ handleStatusResponse(result.product, values[1][0], options);
88
+ } else {
89
+ // Fallback for product quantity on a failed product status call.
90
+ // This is done to prevent the product detail component from locking up unnecessarily.
91
+ result.product.availableQuantity = 10000;
92
+ }
93
+
94
+ return result;
95
+ }
96
+
87
97
  async function getProductDetail(_options) {
88
98
  let options = _options;
89
- let response;
99
+ let url = await getSkuSearchUrl(options);
100
+ let data;
90
101
 
91
102
  // Hit the PMD and get all the data for the product including the price information.
103
+ // Example url: https://test.nuskin.com/content/products/01/00/34/01003440.service.US.json
92
104
  try {
93
- response = await productLib.getProduct({
94
- fromId: options.sku,
95
- env: RunConfigService.getEnvironmentCode(),
96
- market: options.country,
97
- language: options.language
105
+ data = await axios({
106
+ method: 'GET',
107
+ url
98
108
  })
99
109
  } catch (e) {
100
- throw {error: e, failedSku: options.sku, type: events.errors.PRODUCT_LOOKUP};
110
+ throw {error: e, url: url, failedSku: options.sku, type: events.errors.PRODUCT_LOOKUP};
101
111
  }
102
- return response.data;
112
+ return data.data;
103
113
  }
104
114
 
105
115
  function getProductStatus(_options) {
@@ -125,6 +135,36 @@ let ProductService = function() {
125
135
  );
126
136
  }
127
137
 
138
+ async function getSkuSearchUrl(options) {
139
+ let firstTwo = options.sku.substring(0, 2),
140
+ secondTwo = options.sku.substring(2, 4),
141
+ thirdTwo = options.sku.substring(4, 6),
142
+ runConfig = RunConfigService.getRunConfig(),
143
+ query = '',
144
+ skuSearchUrl;
145
+
146
+ const {Url: urlCfg} = await getConfiguration(['Url']);
147
+ skuSearchUrl = urlCfg.skuSearchUrl;
148
+
149
+ if (runConfig.baseUrl !== '' && !skuSearchUrl.startsWith('http')) {
150
+ query = runConfig.baseUrl;
151
+ }
152
+
153
+ if (UserService.isLTOSite()){
154
+ query += util.getSiteConfig().urls.skuSearchUrl[0].replace('{firstTwo}', firstTwo);
155
+ }
156
+ else{
157
+ query += skuSearchUrl.replace('{firstTwo}', firstTwo);
158
+ }
159
+
160
+ query = query.replace('{secondTwo}', secondTwo);
161
+ query = query.replace('{thirdTwo}', thirdTwo);
162
+ query = query.replace('{sku}', options.sku);
163
+ query = query.replace('{countryCd}', options.cntryCd ? options.cntryCd : options.country);
164
+
165
+ return query;
166
+ }
167
+
128
168
  async function handleDetailResponse(responseData, options) {
129
169
  let retVal = {success: true},
130
170
  dataForProduct = createDataFromSkuSearch(responseData, options);