@decocms/apps 0.25.1 → 0.25.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.
@@ -16,5 +16,11 @@ const formatter = (currency: string, locale: string) => {
16
16
  return formatters.get(key)!;
17
17
  };
18
18
 
19
- export const formatPrice = (price: number | undefined, currency = "BRL", locale = "pt-BR") =>
20
- price !== undefined ? formatter(currency, locale).format(price) : null;
19
+ export const formatPrice = (
20
+ price: number | undefined | null,
21
+ currency = "BRL",
22
+ locale = "pt-BR",
23
+ ) =>
24
+ price != null && Number.isFinite(price)
25
+ ? formatter(currency, locale).format(price)
26
+ : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/apps",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "type": "module",
5
5
  "description": "Deco commerce apps for TanStack Start - Shopify, VTEX, commerce types, analytics utils",
6
6
  "exports": {
@@ -294,6 +294,24 @@ export default async function vtexProductListingPage(props: PLPProps): Promise<a
294
294
  }
295
295
  }
296
296
 
297
+ // Handle VTEX `map` query param (e.g. /1368?map=productClusterIds).
298
+ // The `map` param tells IS how to interpret each path segment as a facet type.
299
+ // Segments and map values are positionally matched (comma-separated).
300
+ if (facets.length === 0 && pageUrl && __pagePath) {
301
+ const mapParam = pageUrl.searchParams.get("map");
302
+ if (mapParam) {
303
+ const segments = __pagePath.split("/").filter(Boolean);
304
+ const mapValues = mapParam.split(",");
305
+ for (let i = 0; i < Math.min(segments.length, mapValues.length); i++) {
306
+ const key = mapValues[i].trim();
307
+ const value = decodeURIComponent(segments[i]);
308
+ if (key && value) {
309
+ facets.push({ key, value });
310
+ }
311
+ }
312
+ }
313
+ }
314
+
297
315
  let pageTypes: PageType[] = [];
298
316
 
299
317
  if (
@@ -577,7 +577,7 @@ const SHELF_PROPERTY_NAMES = new Set([
577
577
  *
578
578
  * Differences from toProduct():
579
579
  * - Images: capped at 2 per SKU (front + back)
580
- * - Offers: first seller only, stripped installments (keeps ListPrice, SalePrice, SRP, PIX, best no-interest)
580
+ * - Offers: best seller only (in-stock first, then cheapest), stripped installments (keeps ListPrice, SalePrice, SRP, PIX, best no-interest)
581
581
  * - isVariantOf: single in-stock variant at level 0
582
582
  * - additionalProperty: filtered to known-used property names
583
583
  * - Drops: description, video, isAccessoryOrSparePartFor, alternateName, gtin, releaseDate, model
@@ -603,12 +603,13 @@ export const toProductShelf = <P extends LegacyProductVTEX | ProductVTEX>(
603
603
  }));
604
604
  const finalImages = mappedImages.length > 0 ? mappedImages : [DEFAULT_IMAGE];
605
605
 
606
- // Offers: first seller only, lean
607
- const firstSeller = (sku.sellers ?? [])[0];
608
- const fullOffer = firstSeller
609
- ? (isLegacyProduct(product) ? toOfferLegacy : toOffer)(firstSeller)
610
- : undefined;
611
- const leanOffers = fullOffer ? [buildOfferShelf(fullOffer)] : [];
606
+ // Offers: best seller (in-stock first, then cheapest), lean.
607
+ // Must consider ALL sellers so marketplace products where sellers[0]
608
+ // is OOS but another seller has stock still show as available.
609
+ const offerConverter = isLegacyProduct(product) ? toOfferLegacy : toOffer;
610
+ const allOffers = (sku.sellers ?? []).map(offerConverter).sort(bestOfferFirst);
611
+ const bestOffer = allOffers[0];
612
+ const leanOffers = bestOffer ? [buildOfferShelf(bestOffer)] : [];
612
613
 
613
614
  // isVariantOf: single in-stock variant at level 0
614
615
  const isVariantOf =