@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/cs-product-lib",
3
- "version": "1.0.6-cx12-6632.1",
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
  }
@@ -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
+
@@ -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
- // Each value is a function with a parameter 'parent' that is the current instance of a Product
7
- // ContentStack references need to be queried separately (ie: variants)
8
- id: parent.uid,
9
- slug: parent?.seo_information?.slug,
10
- variantSelectLabel: parent.variant_select_label,
11
- secondaryTitle: parent.secondary_title,
12
- title: parent.name,
13
- productImages: helpers?.getProductImages(parent),
14
- salesLabel: parent.sales_label,
15
- salesText: parent.sales_text,
16
- description: entities.decodeHTML(parent.short_description || ''),
17
- salesDisclaimer: helpers?.mapSalesDisclaimer(parent.sales_disclaimer),
18
- features: helpers?.mapFeatures(parent),
19
- productDetails: helpers?.mapProductDetails(parent),
20
- benefitsMedia: {
21
- image: helpers?.mapImage(parent.benefits_media?.image),
22
- youTubeVideoId: parent.benefits_media?.youtube_video_id
23
- },
24
- benefits: helpers?.mapBenefits(parent),
25
- benefitsAndFeatures: helpers?.mapBenefitsAndFeatures(parent),
26
- bundle: parent?.bundle,
27
- type: parent?.type,
28
- results: helpers?.mapResults(parent),
29
- sustainability: helpers?.mapSustainability(parent),
30
- usage: helpers?.mapUsage(parent, parent.legacy_usage),
31
- resources: helpers?.mapResources(parent),
32
- faqs: helpers?.mapFaqs(parent),
33
- categories: parent.categories?.map((category) => ({
34
- displayName: category.display_name,
35
- id: category.category_id,
36
- subcategories: category.subcategories?.map((subcategory) => ({
37
- displayName: subcategory.display_name,
38
- id: subcategory.category_id
39
- }))
40
- })),
41
- ingredients: helpers?.mapIngredients(
42
- parent.ingredients,
43
- parent.legacy_ingredients
44
- ),
45
- legacyBenefits: parent.legacy_benefits,
46
- legacyLongDescription: parent.legacy_long_description,
47
- legacyResources: parent.legacy_resources,
48
- legacyUsage: parent.legacy_usage,
49
- legacyIngredients: parent.legacy_ingredients,
50
- legacyImages: parent.legacy_images || [],
51
- legacyYouTube: parent.legacy_youtube_video,
52
- disclaimers: parent.disclaimers || [],
53
- disclaimerWarnings: helpers?.mapDisclaimerWarnings(
54
- parent.disclaimer_warnings
55
- ),
56
- seoInformation: helpers?.mapSeoInformation(parent, homeUrl),
57
- variants: helpers?.mapVariants(parent),
58
- refund: helpers?.mapRefund(parent),
59
- shipping: helpers?.mapShipping(parent),
60
- generalDisclaimer: parent.translations?.general_disclaimer_text,
61
- thirdPartyScripts: parent.third_party_scripts
62
- ?.filter((tps) => tps.enabled && !!tps.script)
63
- ?.map((tps) => tps.script),
64
- csAtts: {
65
- version: parent._version || 0,
66
- updatedAt: parent.updated_at || '',
67
- updatedBy: parent.updated_by || '',
68
- createdAt: parent.created_at || '',
69
- createdBy: parent.created_by || '',
70
- locale: parent.locale || ''
71
- },
72
- relevancyRank: parent.relevancy_rank || null,
73
- warranty: helpers?.mapWarranty(parent),
74
- upSellProductIds: helpers?.mapUpSellProductIds(parent),
75
- crossProductIds: helpers?.mapCrossProductIds(parent)
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