Skip to content

Commit 2255cfa

Browse files
committed
Add prices block
1 parent a23662d commit 2255cfa

File tree

5 files changed

+159
-84
lines changed

5 files changed

+159
-84
lines changed

src/content/queries/cs-product.js

+31-27
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,6 @@ import { gql, parseRating, parseSpecialToDate } from '../../utils/product.js';
1919
* @returns {Product}
2020
*/
2121
export const adapter = (config, productData) => {
22-
let minPrice = productData.priceRange?.minimum ?? productData.price;
23-
let maxPrice = productData.priceRange?.maximum ?? productData.price;
24-
25-
if (minPrice == null) {
26-
minPrice = maxPrice;
27-
} else if (maxPrice == null) {
28-
maxPrice = minPrice;
29-
}
3022
/** @type {Product} */
3123
const product = {
3224
sku: productData.sku,
@@ -45,7 +37,12 @@ export const adapter = (config, productData) => {
4537
attributes: productData.attributes ?? [],
4638
attributeMap: Object.fromEntries((productData.attributes ?? [])
4739
.map(({ name, value }) => [name, value])),
48-
options: (productData.options ?? []).map((option) => ({
40+
// eslint-disable-next-line no-underscore-dangle
41+
type: productData.__typename === 'SimpleProductView' ? 'simple' : 'complex',
42+
};
43+
44+
if (productData.options) {
45+
product.options = productData.options.map((option) => ({
4946
id: option.id,
5047
label: option.title,
5148
// eslint-disable-next-line no-underscore-dangle
@@ -62,7 +59,7 @@ export const adapter = (config, productData) => {
6259
? {
6360
sku: value.product.sku,
6461
name: value.product.name,
65-
prices: value.product.price ? {
62+
price: value.product.price ? {
6663
regular: value.product.price.regular,
6764
final: value.product.price.final,
6865
visible: value.product.price.roles?.includes('visible'),
@@ -72,25 +69,31 @@ export const adapter = (config, productData) => {
7269
quantity: value.quantity,
7370
isDefault: value.isDefault,
7471
})),
75-
})),
76-
prices: (minPrice && maxPrice) ? {
77-
regular: {
78-
// TODO: determine whether to use min or max
79-
amount: minPrice.regular.amount.value,
80-
currency: minPrice.regular.amount.currency,
81-
maximumAmount: maxPrice.regular.amount.value,
82-
minimumAmount: minPrice.regular.amount.value,
72+
}));
73+
}
74+
75+
if (productData.price) {
76+
product.price = {
77+
regular: productData.price.regular,
78+
final: productData.price.final,
79+
visible: productData.price.roles?.includes('visible'),
80+
};
81+
}
82+
83+
if (productData.priceRange) {
84+
product.priceRange = {
85+
minimum: {
86+
regular: productData.priceRange.minimum.regular,
87+
final: productData.priceRange.minimum.final,
88+
visible: productData.priceRange.minimum.roles?.includes('visible'),
8389
},
84-
final: {
85-
// TODO: determine whether to use min or max
86-
amount: minPrice.final.amount.value,
87-
currency: minPrice.final.amount.currency,
88-
maximumAmount: maxPrice.final.amount.value,
89-
minimumAmount: minPrice.final.amount.value,
90+
maximum: {
91+
regular: productData.priceRange.maximum.regular,
92+
final: productData.priceRange.maximum.final,
93+
visible: productData.priceRange.maximum.roles?.includes('visible'),
9094
},
91-
visible: minPrice.roles?.includes('visible'),
92-
} : null,
93-
};
95+
};
96+
}
9497

9598
if (config.attributeOverrides?.product) {
9699
Object.entries(config.attributeOverrides.product).forEach(([key, value]) => {
@@ -122,6 +125,7 @@ export default ({ sku, imageRoles = [] }) => gql`{
122125
products(
123126
skus: ["${sku}"]
124127
) {
128+
__typename
125129
id
126130
sku
127131
name

src/content/queries/cs-variants.js

+26-19
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ import { gql, parseRating, parseSpecialToDate } from '../../utils/product.js';
1919
* @returns {Variant[]}
2020
*/
2121
export const adapter = (config, variants) => variants.map(({ selections, product }) => {
22-
const minPrice = product.priceRange?.minimum ?? product.price;
23-
const maxPrice = product.priceRange?.maximum ?? product.price;
24-
2522
/** @type {Variant} */
2623
const variant = {
2724
name: product.name,
@@ -34,25 +31,34 @@ export const adapter = (config, variants) => variants.map(({ selections, product
3431
attributeMap: Object.fromEntries((product.attributes ?? [])
3532
.map(({ name, value }) => [name, value])),
3633
externalId: product.externalId,
37-
prices: {
38-
regular: {
39-
// TODO: determine whether to use min or max
40-
amount: minPrice?.regular.amount.value,
41-
currency: minPrice?.regular.amount.currency,
42-
maximumAmount: maxPrice?.regular.amount.value,
43-
minimumAmount: minPrice?.regular.amount.value,
44-
},
45-
final: {
46-
// TODO: determine whether to use min or max
47-
amount: minPrice?.final.amount.value,
48-
currency: minPrice?.final.amount.currency,
49-
maximumAmount: maxPrice?.final.amount.value,
50-
minimumAmount: minPrice?.final.amount.value,
51-
},
52-
},
5334
selections: (selections ?? []).sort(),
35+
// eslint-disable-next-line no-underscore-dangle
36+
type: product.__typename === 'SimpleProductView' ? 'simple' : 'complex',
5437
};
5538

39+
if (product.price) {
40+
variant.price = {
41+
regular: product.price.regular,
42+
final: product.price.final,
43+
visible: product.price.roles?.includes('visible'),
44+
};
45+
}
46+
47+
if (product.priceRange) {
48+
variant.priceRange = {
49+
minimum: {
50+
regular: product.priceRange.minimum.regular,
51+
final: product.priceRange.minimum.final,
52+
visible: product.priceRange.minimum.roles?.includes('visible'),
53+
},
54+
maximum: {
55+
regular: product.priceRange.maximum.regular,
56+
final: product.priceRange.maximum.final,
57+
visible: product.priceRange.maximum.roles?.includes('visible'),
58+
},
59+
};
60+
}
61+
5662
if (config.attributeOverrides?.variant) {
5763
Object.entries(config.attributeOverrides.variant).forEach(([key, value]) => {
5864
variant.attributeMap[key] = variant.attributeMap[value] ?? variant[key];
@@ -84,6 +90,7 @@ export default ({ sku, imageRoles = [] }) => gql`
8490
variants {
8591
selections
8692
product {
93+
__typename
8794
name
8895
sku
8996
inStock

src/templates/html/HTMLTemplate.js

+55-11
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class HTMLTemplate {
3838
* @param {number|undefined} max
3939
* @returns {string}
4040
*/
41-
static priceRange = (min, max) => (min !== max ? ` (${min} - ${max})` : '');
41+
static priceRange = (min, max) => (min !== max ? `${min} - ${max}` : `${min}`);
4242

4343
/**
4444
* @param {string} str
@@ -108,7 +108,7 @@ ${HTMLTemplate.metaName('twitter:card', 'summary_large_image')}
108108
${HTMLTemplate.metaName('twitter:title', product.name)}
109109
${HTMLTemplate.metaName('twitter:description', product.metaDescription)}
110110
${HTMLTemplate.metaName('twitter:label1', 'Price')}
111-
${HTMLTemplate.metaName('twitter:data1', product.prices?.final?.amount)}
111+
${HTMLTemplate.metaName('twitter:data1', product.price?.final?.amount?.value ?? product.priceRange?.minimum?.final?.amount?.value)}
112112
${HTMLTemplate.metaName('twitter:label2', 'Availability')}
113113
${HTMLTemplate.metaName('twitter:data2', product.inStock ? 'In stock' : 'Out of stock')}`;
114114
}
@@ -126,8 +126,9 @@ ${HTMLTemplate.metaName('externalId', product.externalId)}
126126
${HTMLTemplate.metaName('addToCartAllowed', product.addToCartAllowed)}
127127
${HTMLTemplate.metaName('inStock', product.inStock ? 'true' : 'false')}
128128
${HTMLTemplate.metaProperty('product:availability', product.inStock ? 'In stock' : 'Out of stock')}
129-
${HTMLTemplate.metaProperty('product:price.amount', product.prices?.final?.amount)}
130-
${HTMLTemplate.metaProperty('product:price.currency', product.prices?.final?.currency)}`;
129+
${HTMLTemplate.metaProperty('product:price.amount', product.price?.final?.amount?.value ?? product.priceRange?.minimum?.final?.amount?.value)}
130+
${HTMLTemplate.metaProperty('product:price.currency', product.price?.final?.amount?.currency ?? product.priceRange?.minimum?.final?.amount?.currency)}
131+
${HTMLTemplate.metaProperty('product:type', product.type)}`;
131132
}
132133

133134
/**
@@ -169,6 +170,38 @@ ${HTMLTemplate.indent(this.renderJSONLD(), 2)}
169170
</head>`;
170171
}
171172

173+
/**
174+
* Render product price block
175+
* @param {Product} product
176+
* @returns {string}
177+
*/
178+
renderProductPrices(product) {
179+
const { price, priceRange } = product;
180+
const hasFinal = price
181+
? price.final?.amount?.value < price.regular?.amount?.value
182+
: priceRange?.minimum?.final.amount.value < priceRange?.minimum?.regular.amount.value;
183+
const isRange = !!priceRange
184+
&& priceRange.minimum?.final?.amount?.value !== priceRange.maximum?.final?.amount?.value;
185+
186+
const regularPrice = price ? price.regular?.amount : priceRange.minimum?.regular?.amount;
187+
const finalPrice = price ? price.final?.amount : priceRange.minimum?.final?.amount;
188+
189+
return /* html */ `\
190+
<div class="product-prices">
191+
<div>
192+
<div>Regular</div>
193+
<div>${regularPrice.value} ${regularPrice.currency}</div>
194+
${isRange ? `<div>${priceRange.maximum?.regular?.amount?.value} ${priceRange.maximum?.regular?.amount?.currency}</div>` : ''}
195+
</div>
196+
${hasFinal ? /* html */ `\
197+
<div>
198+
<div>${finalPrice.value} ${finalPrice.currency}</div>
199+
<div>${price ? price.final?.amount?.value : priceRange.minimum?.final?.amount?.value}</div>
200+
${isRange ? `<div>${priceRange.maximum?.final?.amount?.value} ${priceRange.maximum?.final?.amount?.currency}</div>` : ''}
201+
</div>` : ''}
202+
</div>`;
203+
}
204+
172205
/**
173206
* Create the product images
174207
* @param {Image[]} images
@@ -235,7 +268,7 @@ ${attributes.map((attr) => /* html */`\
235268
* @returns {string}
236269
*/
237270
renderProductOptions(options) {
238-
return options.length > 0 ? /* html */ `\
271+
return options?.length > 0 ? /* html */ `\
239272
<div class="product-options">
240273
${options.map((opt) => /* html */ `\
241274
<div>
@@ -302,13 +335,23 @@ ${HTMLTemplate.indent(this.renderProductItems(opt.items), 2)}`).join('\n')}
302335

303336
/**
304337
* Create the variant prices
305-
* @param {Pick<Prices, 'regular' | 'final'>} prices
338+
* @param {Variant} variant
306339
* @returns {string}
307340
*/
308-
renderVariantPrices(prices) {
309-
return /* html */ `\
310-
<div>Regular: ${prices.regular?.amount} ${prices.regular?.currency}${HTMLTemplate.priceRange(prices.regular?.minimumAmount, prices.regular?.maximumAmount)}</div>
311-
<div>Final: ${prices.final?.amount} ${prices.final?.currency}${HTMLTemplate.priceRange(prices.final?.minimumAmount, prices.final?.maximumAmount)}</div>`;
341+
renderVariantPrices(variant) {
342+
if (variant.price) {
343+
const { price } = variant;
344+
return /* html */ `\
345+
<div>Regular: ${price.regular?.amount?.value} ${price.regular?.amount?.currency}</div>
346+
<div>Final: ${price.final?.amount?.value} ${price.final?.amount?.currency}</div>`;
347+
}
348+
if (variant.priceRange) {
349+
const { minimum, maximum } = variant.priceRange;
350+
return /* html */ `\
351+
<div>Regular: ${HTMLTemplate.priceRange(minimum?.regular?.amount?.value, maximum?.regular?.amount?.value)} ${minimum?.regular?.amount?.currency}</div>
352+
<div>Final: ${HTMLTemplate.priceRange(minimum?.final?.amount?.value, maximum?.final?.amount?.value)} ${minimum?.final?.amount?.currency}</div>`;
353+
}
354+
return '';
312355
}
313356

314357
/**
@@ -328,7 +371,7 @@ ${this.variants.map((v) => /* html */`\
328371
<div>${v.name}</div>
329372
<div>${v.description}</div>
330373
<div>${v.inStock ? 'inStock' : ''}</div>
331-
${v.prices ? HTMLTemplate.indent(this.renderVariantPrices(v.prices), 4) : ''}
374+
${v.price || v.priceRange ? HTMLTemplate.indent(this.renderVariantPrices(v), 4) : ''}
332375
<div>
333376
${HTMLTemplate.indent(this.renderVariantImages(v.images), 6)}
334377
</div>
@@ -389,6 +432,7 @@ ${HTMLTemplate.indent(this.renderHead(), 2)}
389432
<h1>${name}</h1>
390433
${description ? `<p>${description}</p>` : ''}
391434
${HTMLTemplate.indent(this.renderProductImages(images), 8)}
435+
${HTMLTemplate.indent(this.renderProductPrices(this.product), 8)}
392436
${HTMLTemplate.indent(this.renderProductAttributes(attributes), 8)}
393437
${HTMLTemplate.indent(this.renderProductOptions(options), 8)}
394438
${HTMLTemplate.indent(this.renderProductVariants(), 8)}

src/templates/json/JSONTemplate.js

+20-12
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,21 @@ export class JSONTemplate {
126126
const configurableProduct = this.variants?.length > 0;
127127
const offers = configurableProduct ? this.variants : [this.product];
128128
return offers.map((v) => {
129-
const { prices: variantPrices } = v;
130129
const offerUrl = this.constructProductURL(configurableProduct ? v : undefined);
131130
const mpn = this.constructMPN(configurableProduct ? v : undefined);
132-
const finalPrice = variantPrices?.final?.amount;
133-
const regularPrice = variantPrices?.regular?.amount;
131+
132+
let finalPrice;
133+
let regularPrice;
134+
let currency;
135+
if (v.price) {
136+
finalPrice = v.price.final.amount.value;
137+
regularPrice = v.price.regular.amount.value;
138+
currency = v.price.final.amount.currency;
139+
} else if (v.priceRange) {
140+
finalPrice = v.priceRange.minimum.final.amount.value;
141+
regularPrice = v.priceRange.minimum.regular.amount.value;
142+
currency = v.priceRange.minimum.final.amount.currency;
143+
}
134144

135145
const offer = {
136146
'@type': 'Offer',
@@ -140,31 +150,29 @@ export class JSONTemplate {
140150
image: v.images?.[0]?.url ?? image,
141151
availability: v.inStock ? 'InStock' : 'OutOfStock',
142152
price: finalPrice,
143-
priceCurrency: variantPrices?.final?.currency,
153+
priceCurrency: currency,
144154
gtin: v.attributeMap.gtin,
145155
priceValidUntil: v.specialToDate,
146156
aggregateRating: this.renderRating(v),
147157
};
148158

149-
if (variantPrices) {
150-
if (finalPrice < regularPrice) {
151-
offer.priceSpecification = this.renderOffersPriceSpecification(v);
152-
}
159+
if (finalPrice < regularPrice) {
160+
offer.priceSpecification = this.renderOffersPriceSpecification(regularPrice, currency);
153161
}
154162

155163
return pruneUndefined(offer);
156164
});
157165
}
158166

159167
/**
160-
* @param {Variant} variant
168+
* @param {number} regularPrice
169+
* @param {string} currency
161170
*/
162-
renderOffersPriceSpecification(variant) {
163-
const { prices: { regular: { amount, currency } } } = variant;
171+
renderOffersPriceSpecification(regularPrice, currency) {
164172
return {
165173
'@type': 'UnitPriceSpecification',
166174
priceType: 'https://schema.org/ListPrice',
167-
price: amount,
175+
price: regularPrice,
168176
priceCurrency: currency,
169177
};
170178
}

0 commit comments

Comments
 (0)