@nuskin/cs-product-lib 1.0.6-cx12-6632.1 → 1.0.6
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 +2 -1
- package/src/formatKit.js +169 -0
- package/src/formatProduct.js +76 -72
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuskin/cs-product-lib",
|
|
3
|
-
"version": "1.0.6
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "Library for pulling and parsing product content (and applying business logic) retrieved from Contentstack",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"config": "^3.3.6",
|
|
44
44
|
"entities": "4.4.0",
|
|
45
45
|
"lodash": "4.17.21",
|
|
46
|
+
"moment": "^2.29.4",
|
|
46
47
|
"turndown": "7.1.1"
|
|
47
48
|
}
|
|
48
49
|
}
|
package/src/formatKit.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
const moment = require('moment')
|
|
2
|
+
|
|
3
|
+
const parseStatus = (variant) => {
|
|
4
|
+
const dateFormat = 'YYYY-MM-DD'
|
|
5
|
+
const properties = variant.properties || {}
|
|
6
|
+
if (
|
|
7
|
+
variant.inventoryProperties &&
|
|
8
|
+
variant.inventoryProperties.backOrdered &&
|
|
9
|
+
variant.inventoryProperties.expectedBackOrderAvailabilityDate) {
|
|
10
|
+
return {
|
|
11
|
+
|
|
12
|
+
// changed
|
|
13
|
+
status: 'inactive',
|
|
14
|
+
isBackordered: variant.inventoryProperties.backOrdered,
|
|
15
|
+
backorderedAvailableDate: moment(variant.inventoryProperties.expectedBackOrderAvailabilityDate).format(dateFormat)
|
|
16
|
+
}
|
|
17
|
+
} else if(properties.productStatus) {
|
|
18
|
+
let isBackordered = false
|
|
19
|
+
let backorderedAvailableDate = null
|
|
20
|
+
if (variant.skuKit) {
|
|
21
|
+
try {
|
|
22
|
+
const latestBODate = Math.min(...variant.skuKit.flatMap(a=>a.sku.map(s=>s.inventoryProperties.expectedBackOrderAvailabilityDate))) || 0
|
|
23
|
+
if (latestBODate) {
|
|
24
|
+
isBackordered = true
|
|
25
|
+
backorderedAvailableDate = moment(Date(latestBODate)).format(dateFormat)
|
|
26
|
+
}
|
|
27
|
+
} catch {
|
|
28
|
+
backorderedAvailableDate = null
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
} else if (variant.bundle) {
|
|
32
|
+
try {
|
|
33
|
+
const latestBODate = Math.min(...variant.bundleMandatoryProducts.flatMap(a=>a.sku.map(s=>s.inventoryProperties.expectedBackOrderAvailabilityDate))) || 0
|
|
34
|
+
if (latestBODate) {
|
|
35
|
+
isBackordered = true
|
|
36
|
+
backorderedAvailableDate = moment(Date(latestBODate)).format(dateFormat)
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
backorderedAvailableDate = null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
// changed
|
|
44
|
+
status: 'active',
|
|
45
|
+
isBackordered,
|
|
46
|
+
backorderedAvailableDate
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
// changed
|
|
51
|
+
status: 'active',
|
|
52
|
+
isBackordered: false,
|
|
53
|
+
backorderedAvailableDate: null
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const parsePrice = (variant) => {
|
|
58
|
+
const price = {}
|
|
59
|
+
const points = {}
|
|
60
|
+
price.currencyCode = (variant.prices || [])?.[0]?.currencyCode || (variant?.prices || [])?.[0]?.currencySymbol
|
|
61
|
+
const priceFacets = variant?.priceFacets || {}
|
|
62
|
+
price.retail = priceFacets['Regular Price'] || ((variant?.prices || [])[0] || {}).price
|
|
63
|
+
price.wholesale = priceFacets['Wholesale Price'] || ((variant?.prices || [])[0] || {}).price
|
|
64
|
+
price.retailSubscription = priceFacets['Regular Price'] || ((variant?.prices || [])[0] || {}).price
|
|
65
|
+
price.wholesaleSubscription = priceFacets['Wholesale Price'] || ((variant?.prices || [])[0] || {}).price
|
|
66
|
+
points.wholesale = {cv: priceFacets.CV, pv: priceFacets.PV, sb: priceFacets.SB}
|
|
67
|
+
// Where do we pull subscription price/points from?
|
|
68
|
+
points.subscription = {cv: priceFacets.CV, pv: priceFacets.PV, sb: priceFacets.SB}
|
|
69
|
+
return {price, points}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const parseKitAvailableQuantity = (skuKit) => {
|
|
73
|
+
try {
|
|
74
|
+
return Math.min(...skuKit.flatMap(a=>a.sku.map(s=>s.inventoryProperties.atpQty))) || 0
|
|
75
|
+
} catch {
|
|
76
|
+
return 0
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const parseBundleAvailableQuantity = (product) => {
|
|
81
|
+
try {
|
|
82
|
+
const {requiredProducts = []} = product|| {}
|
|
83
|
+
const quantities = requiredProducts?.map(p => p?.quantity || 0)
|
|
84
|
+
return Math.min(...quantities) || 0
|
|
85
|
+
} catch {
|
|
86
|
+
console.log("some error in availableQuantity")
|
|
87
|
+
return 0
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const parseKit = (product, market) => {
|
|
92
|
+
const kits = {}
|
|
93
|
+
const [properties] = product || []
|
|
94
|
+
kits.description = properties?.description
|
|
95
|
+
kits.availableChannels = properties.availableChannels
|
|
96
|
+
kits.chargeShipping = properties.chargeShipping === 'true'
|
|
97
|
+
kits.customerTypes = properties.customerTypes
|
|
98
|
+
kits.dangerousGoods = properties.dangerousGoods === 'true'
|
|
99
|
+
// missing
|
|
100
|
+
kits.excludeFromSearch = properties.excludeFromSearch === 'true'
|
|
101
|
+
kits.isExclusive = properties.isExclusive === 'true'
|
|
102
|
+
kits.marketAttributes = properties?.marketAttributes
|
|
103
|
+
kits.maxQuantity = (product.inventoryProperties || {}).atpQty
|
|
104
|
+
const priceData = parsePrice(product)
|
|
105
|
+
kits.points = priceData?.points
|
|
106
|
+
kits.price = priceData?.price
|
|
107
|
+
kits.purchaseTypes = {
|
|
108
|
+
buyOnce: kits?.availableChannels?.includes('web'),
|
|
109
|
+
subscription: kits?.availableChannels?.includes('subscription')
|
|
110
|
+
}
|
|
111
|
+
kits.retailDiscount= properties?.retailDiscount
|
|
112
|
+
kits.wholesaleDiscount = properties?.wholesaleDiscount
|
|
113
|
+
kits.pvDiscount = properties?.pvDiscount
|
|
114
|
+
kits.cvDiscount = properties?.cvDiscount
|
|
115
|
+
kits.sbDiscount = properties?.sbDiscount
|
|
116
|
+
kits.grpDiscount = properties?.grpDiscount
|
|
117
|
+
kits.scanQualifiedCount = properties?.scanQualifiedCount
|
|
118
|
+
// newVariant.slug = undefined
|
|
119
|
+
kits.status = parseStatus(product)
|
|
120
|
+
if ((kits.status || {}).status === 'active') kits.status.status = 'sellable'
|
|
121
|
+
const kitProducts = []
|
|
122
|
+
if (properties.type === 'kit') {
|
|
123
|
+
kits.type = 'skuKit'
|
|
124
|
+
kits.availableQuantity = parseKitAvailableQuantity(product.skuKit)
|
|
125
|
+
for (const kit of product.skuKit) {
|
|
126
|
+
const kitProduct = formatKit(kit, market)
|
|
127
|
+
kitProduct.id = kit.identifier
|
|
128
|
+
kitProducts.push({product: kitProduct, quantity: (kit.properties || {}).quantity || 0, isMandatory:true})
|
|
129
|
+
}
|
|
130
|
+
} else if (properties.type === 'bundle') {
|
|
131
|
+
kits.type = 'bundle'
|
|
132
|
+
kits.availableQuantity = parseBundleAvailableQuantity(properties)
|
|
133
|
+
for (const kit of properties?.requiredProducts || []) {
|
|
134
|
+
const kitProduct = formatKit(kit?.product, market)
|
|
135
|
+
kitProduct.id = kit.product.id
|
|
136
|
+
kitProducts.push({product: kitProduct, quantity: kit?.quantity || 0, isMandatory:true})
|
|
137
|
+
}
|
|
138
|
+
for (const kit of properties?.optionalProducts || []) {
|
|
139
|
+
const kitProduct = formatKit(kit?.product, market)
|
|
140
|
+
kitProduct.id = kit.product.id
|
|
141
|
+
kitProducts.push({product: kitProduct, quantity: kit?.quantity || 0, isMandatory:false})
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
kits.kitProducts = kitProducts
|
|
145
|
+
return kits
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const formatKit = (product, market) => {
|
|
149
|
+
|
|
150
|
+
if (!product) {
|
|
151
|
+
return {}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
let kit = null
|
|
155
|
+
if(['kit', 'bundle'].includes(product.type)) {
|
|
156
|
+
kit = parseKit(product.bundle, market)
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
...product,
|
|
160
|
+
bundle: kit
|
|
161
|
+
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
module.exports = {
|
|
167
|
+
formatKit
|
|
168
|
+
}
|
|
169
|
+
|
package/src/formatProduct.js
CHANGED
|
@@ -1,79 +1,83 @@
|
|
|
1
1
|
const helpers = require('./helpers')
|
|
2
2
|
const entities = require('entities')
|
|
3
|
+
const { formatKit } = require('./formatKit')
|
|
3
4
|
|
|
4
5
|
/* istanbul ignore next */
|
|
5
|
-
const formatProduct = (parent, homeUrl) =>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
6
|
+
const formatProduct = (parent, homeUrl) => {
|
|
7
|
+
const formatedProduct = {
|
|
8
|
+
// Each value is a function with a parameter 'parent' that is the current instance of a Product
|
|
9
|
+
// ContentStack references need to be queried separately (ie: variants)
|
|
10
|
+
id: parent.uid,
|
|
11
|
+
slug: parent?.seo_information?.slug,
|
|
12
|
+
variantSelectLabel: parent.variant_select_label,
|
|
13
|
+
secondaryTitle: parent.secondary_title,
|
|
14
|
+
title: parent.name,
|
|
15
|
+
productImages: helpers?.getProductImages(parent),
|
|
16
|
+
salesLabel: parent.sales_label,
|
|
17
|
+
salesText: parent.sales_text,
|
|
18
|
+
description: entities.decodeHTML(parent.short_description || ''),
|
|
19
|
+
salesDisclaimer: helpers?.mapSalesDisclaimer(parent.sales_disclaimer),
|
|
20
|
+
features: helpers?.mapFeatures(parent),
|
|
21
|
+
productDetails: helpers?.mapProductDetails(parent),
|
|
22
|
+
benefitsMedia: {
|
|
23
|
+
image: helpers?.mapImage(parent.benefits_media?.image),
|
|
24
|
+
youTubeVideoId: parent.benefits_media?.youtube_video_id
|
|
25
|
+
},
|
|
26
|
+
benefits: helpers?.mapBenefits(parent),
|
|
27
|
+
benefitsAndFeatures: helpers?.mapBenefitsAndFeatures(parent),
|
|
28
|
+
bundle: parent?.bundle,
|
|
29
|
+
type: parent?.type,
|
|
30
|
+
results: helpers?.mapResults(parent),
|
|
31
|
+
sustainability: helpers?.mapSustainability(parent),
|
|
32
|
+
usage: helpers?.mapUsage(parent, parent.legacy_usage),
|
|
33
|
+
resources: helpers?.mapResources(parent),
|
|
34
|
+
faqs: helpers?.mapFaqs(parent),
|
|
35
|
+
categories: parent.categories?.map((category) => ({
|
|
36
|
+
displayName: category.display_name,
|
|
37
|
+
id: category.category_id,
|
|
38
|
+
subcategories: category.subcategories?.map((subcategory) => ({
|
|
39
|
+
displayName: subcategory.display_name,
|
|
40
|
+
id: subcategory.category_id
|
|
41
|
+
}))
|
|
42
|
+
})),
|
|
43
|
+
ingredients: helpers?.mapIngredients(
|
|
44
|
+
parent.ingredients,
|
|
45
|
+
parent.legacy_ingredients
|
|
46
|
+
),
|
|
47
|
+
legacyBenefits: parent.legacy_benefits,
|
|
48
|
+
legacyLongDescription: parent.legacy_long_description,
|
|
49
|
+
legacyResources: parent.legacy_resources,
|
|
50
|
+
legacyUsage: parent.legacy_usage,
|
|
51
|
+
legacyIngredients: parent.legacy_ingredients,
|
|
52
|
+
legacyImages: parent.legacy_images || [],
|
|
53
|
+
legacyYouTube: parent.legacy_youtube_video,
|
|
54
|
+
disclaimers: parent.disclaimers || [],
|
|
55
|
+
disclaimerWarnings: helpers?.mapDisclaimerWarnings(
|
|
56
|
+
parent.disclaimer_warnings
|
|
57
|
+
),
|
|
58
|
+
seoInformation: helpers?.mapSeoInformation(parent, homeUrl),
|
|
59
|
+
variants: helpers?.mapVariants(parent),
|
|
60
|
+
refund: helpers?.mapRefund(parent),
|
|
61
|
+
shipping: helpers?.mapShipping(parent),
|
|
62
|
+
generalDisclaimer: parent.translations?.general_disclaimer_text,
|
|
63
|
+
thirdPartyScripts: parent.third_party_scripts
|
|
64
|
+
?.filter((tps) => tps.enabled && !!tps.script)
|
|
65
|
+
?.map((tps) => tps.script),
|
|
66
|
+
csAtts: {
|
|
67
|
+
version: parent._version || 0,
|
|
68
|
+
updatedAt: parent.updated_at || '',
|
|
69
|
+
updatedBy: parent.updated_by || '',
|
|
70
|
+
createdAt: parent.created_at || '',
|
|
71
|
+
createdBy: parent.created_by || '',
|
|
72
|
+
locale: parent.locale || ''
|
|
73
|
+
},
|
|
74
|
+
relevancyRank: parent.relevancy_rank || null,
|
|
75
|
+
warranty: helpers?.mapWarranty(parent),
|
|
76
|
+
upSellProductIds: helpers?.mapUpSellProductIds(parent),
|
|
77
|
+
crossProductIds: helpers?.mapCrossProductIds(parent)
|
|
78
|
+
}
|
|
79
|
+
return formatKit(formatedProduct)
|
|
80
|
+
}
|
|
77
81
|
|
|
78
82
|
module.exports = {
|
|
79
83
|
formatProduct
|