@propeller-commerce/propeller-v2-react-ui 0.18.0 → 0.19.0

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/dist/pure.d.cts CHANGED
@@ -252,9 +252,22 @@ interface OrderItemCardProps {
252
252
  * This component is RSC-safe (no hooks, no context), so the host has to pass
253
253
  * it — there is nothing to read it from. Without it a non-default storefront
254
254
  * renders default-language names and, worse, default-language slugs in the
255
- * links (PWP-940).
255
+ * links.
256
256
  */
257
257
  language?: string;
258
+ /**
259
+ * URL builders from the host's configuration. Supply them and the item link
260
+ * is generated the same way every other link on the storefront is — which is
261
+ * what carries the locale prefix. Without it the component falls back to a
262
+ * bare `/product/:id/:slug`, and on a prefixed storefront that link drops the
263
+ * visitor onto the default language.
264
+ */
265
+ configuration?: {
266
+ urls?: {
267
+ getProductUrl?: (product: Product, language?: string) => string;
268
+ getClusterUrl?: (cluster: Cluster, language?: string) => string;
269
+ };
270
+ };
258
271
  /** Should the item title be a link to the PDP */
259
272
  titleLinkable?: boolean;
260
273
  /** Display a small thumbnail of the order item */
@@ -297,6 +310,12 @@ declare function OrderItemCard(props: OrderItemCardProps): React.JSX.Element;
297
310
  */
298
311
 
299
312
  interface OrderSummaryProps {
313
+ /**
314
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
315
+ * rendered in — without it they fall back to Dutch separators regardless of
316
+ * the language the shopper is reading.
317
+ */
318
+ language?: string;
300
319
  /** The order object from propeller-sdk-v2 */
301
320
  order: any;
302
321
  /** The CSS class for the order summary container */
@@ -360,6 +379,12 @@ declare function OrderSummary(props: OrderSummaryProps): React.JSX.Element;
360
379
  */
361
380
 
362
381
  interface OrderTotalsProps {
382
+ /**
383
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
384
+ * rendered in — without it they fall back to Dutch separators regardless of
385
+ * the language the shopper is reading.
386
+ */
387
+ language?: string;
363
388
  /** The order/quote used to populate the summary data */
364
389
  order: Order;
365
390
  /** Order summary block title */
@@ -397,6 +422,12 @@ declare function OrderTotals(props: OrderTotalsProps): React.JSX.Element;
397
422
  */
398
423
 
399
424
  interface ProductBulkPricesProps {
425
+ /**
426
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
427
+ * rendered in — without it they fall back to Dutch separators regardless of
428
+ * the language the shopper is reading.
429
+ */
430
+ language?: string;
400
431
  /**
401
432
  * Bulk price tiers from the product.
402
433
  * Obtain from `product.bulkPrices`.
@@ -473,6 +504,12 @@ declare function ProductDownloads(props: ProductDownloadsProps): React.JSX.Eleme
473
504
  */
474
505
 
475
506
  interface ProductPriceProps {
507
+ /**
508
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
509
+ * rendered in — without it they fall back to Dutch separators regardless of
510
+ * the language the shopper is reading.
511
+ */
512
+ language?: string;
476
513
  /**
477
514
  * ProductPrice object from the product.
478
515
  * Obtain from `product.price`.
package/dist/pure.d.ts CHANGED
@@ -252,9 +252,22 @@ interface OrderItemCardProps {
252
252
  * This component is RSC-safe (no hooks, no context), so the host has to pass
253
253
  * it — there is nothing to read it from. Without it a non-default storefront
254
254
  * renders default-language names and, worse, default-language slugs in the
255
- * links (PWP-940).
255
+ * links.
256
256
  */
257
257
  language?: string;
258
+ /**
259
+ * URL builders from the host's configuration. Supply them and the item link
260
+ * is generated the same way every other link on the storefront is — which is
261
+ * what carries the locale prefix. Without it the component falls back to a
262
+ * bare `/product/:id/:slug`, and on a prefixed storefront that link drops the
263
+ * visitor onto the default language.
264
+ */
265
+ configuration?: {
266
+ urls?: {
267
+ getProductUrl?: (product: Product, language?: string) => string;
268
+ getClusterUrl?: (cluster: Cluster, language?: string) => string;
269
+ };
270
+ };
258
271
  /** Should the item title be a link to the PDP */
259
272
  titleLinkable?: boolean;
260
273
  /** Display a small thumbnail of the order item */
@@ -297,6 +310,12 @@ declare function OrderItemCard(props: OrderItemCardProps): React.JSX.Element;
297
310
  */
298
311
 
299
312
  interface OrderSummaryProps {
313
+ /**
314
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
315
+ * rendered in — without it they fall back to Dutch separators regardless of
316
+ * the language the shopper is reading.
317
+ */
318
+ language?: string;
300
319
  /** The order object from propeller-sdk-v2 */
301
320
  order: any;
302
321
  /** The CSS class for the order summary container */
@@ -360,6 +379,12 @@ declare function OrderSummary(props: OrderSummaryProps): React.JSX.Element;
360
379
  */
361
380
 
362
381
  interface OrderTotalsProps {
382
+ /**
383
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
384
+ * rendered in — without it they fall back to Dutch separators regardless of
385
+ * the language the shopper is reading.
386
+ */
387
+ language?: string;
363
388
  /** The order/quote used to populate the summary data */
364
389
  order: Order;
365
390
  /** Order summary block title */
@@ -397,6 +422,12 @@ declare function OrderTotals(props: OrderTotalsProps): React.JSX.Element;
397
422
  */
398
423
 
399
424
  interface ProductBulkPricesProps {
425
+ /**
426
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
427
+ * rendered in — without it they fall back to Dutch separators regardless of
428
+ * the language the shopper is reading.
429
+ */
430
+ language?: string;
400
431
  /**
401
432
  * Bulk price tiers from the product.
402
433
  * Obtain from `product.bulkPrices`.
@@ -473,6 +504,12 @@ declare function ProductDownloads(props: ProductDownloadsProps): React.JSX.Eleme
473
504
  */
474
505
 
475
506
  interface ProductPriceProps {
507
+ /**
508
+ * Storefront language ('EN', 'NL', …). Decides the number format prices are
509
+ * rendered in — without it they fall back to Dutch separators regardless of
510
+ * the language the shopper is reading.
511
+ */
512
+ language?: string;
476
513
  /**
477
514
  * ProductPrice object from the product.
478
515
  * Obtain from `product.price`.
package/dist/pure.js CHANGED
@@ -1,4 +1,4 @@
1
- import { buildClusterJsonLd, safeJsonStringify, buildItemListJsonLd, buildProductJsonLd, getLabel, getLocalizedValue, isContentHidden, formatPrice, getCountryName } from '@propeller-commerce/propeller-v2-core-ui';
1
+ import { buildClusterJsonLd, safeJsonStringify, buildItemListJsonLd, buildProductJsonLd, getLabel, getLocalizedValue, isContentHidden, formatPrice, localeForLanguage, getCountryName } from '@propeller-commerce/propeller-v2-core-ui';
2
2
  import { clsx } from 'clsx';
3
3
  import { twMerge } from 'tailwind-merge';
4
4
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
@@ -247,20 +247,21 @@ var ItemStock_default = React.memo(ItemStock);
247
247
  function getProductName(orderItem, language) {
248
248
  return getLocalizedValue(orderItem?.product?.names, language, "") || orderItem?.name || "Unknown Product";
249
249
  }
250
- function getClusterUrl(orderItem, language) {
250
+ function getClusterUrl(orderItem, language, urls) {
251
251
  const cluster = orderItem?.product?.cluster;
252
252
  if (!cluster) return "";
253
253
  const id = cluster.clusterId;
254
254
  const slug = getLocalizedValue(cluster.slugs, language, "");
255
255
  if (!id || !slug) return "";
256
- return `/cluster/${id}/${slug}`;
256
+ return urls?.getClusterUrl?.(cluster, language) || `/cluster/${id}/${slug}`;
257
257
  }
258
- function getProductUrl(orderItem, language) {
259
- const cu = getClusterUrl(orderItem, language);
258
+ function getProductUrl(orderItem, language, urls) {
259
+ const cu = getClusterUrl(orderItem, language, urls);
260
260
  if (cu) return cu;
261
- const id = orderItem?.product?.productId;
262
- const slug = getLocalizedValue(orderItem?.product?.slugs, language, "");
263
- if (id && slug) return `/product/${id}/${slug}`;
261
+ const product = orderItem?.product;
262
+ const id = product?.productId;
263
+ const slug = getLocalizedValue(product?.slugs, language, "");
264
+ if (id && slug) return urls?.getProductUrl?.(product, language) || `/product/${id}/${slug}`;
264
265
  return "";
265
266
  }
266
267
  function OrderItemCard(props) {
@@ -277,7 +278,7 @@ function OrderItemCard(props) {
277
278
  const productName = getProductName(item, props.language);
278
279
  const productSku = item?.product?.sku || item?.sku || "";
279
280
  const productImage = item?.product?.media?.images?.items?.[0]?.imageVariants?.[0]?.url || "";
280
- const productUrl = getProductUrl(item, props.language);
281
+ const productUrl = getProductUrl(item, props.language, props.configuration?.urls);
281
282
  const quantity = item?.quantity || 0;
282
283
  const priceTotal = item?.priceTotal || 0;
283
284
  const discount = item?.discount || 0;
@@ -290,7 +291,7 @@ function OrderItemCard(props) {
290
291
  return props.formatPrice(price);
291
292
  }
292
293
  if (!price && price !== 0) return "-";
293
- return formatPrice(price, { symbol: props.currency ?? "\u20AC" });
294
+ return formatPrice(price, { symbol: props.currency ?? "\u20AC", locale: localeForLanguage(props.language) });
294
295
  }
295
296
  const discountDisplay = discountPercentage > 0 ? `${formatItemPrice(discount)} (${discountPercentage.toFixed(2).replace(".", ",")}%)` : formatItemPrice(discount);
296
297
  return /* @__PURE__ */ jsxs("tbody", { className: "propeller-order-item-card", children: [
@@ -431,7 +432,7 @@ function OrderSummary(props) {
431
432
  if (props.formatPrice) {
432
433
  return props.formatPrice(price);
433
434
  }
434
- return formatPrice(price || 0, { symbol: props.currency ?? "\u20AC" });
435
+ return formatPrice(price || 0, { symbol: props.currency ?? "\u20AC", locale: localeForLanguage(props.language) });
435
436
  }
436
437
  function numericDate(dateString) {
437
438
  const d = new Date(dateString);
@@ -539,7 +540,7 @@ function OrderTotals(props) {
539
540
  const postageData = props.order?.postageData;
540
541
  function formatItemPrice(price) {
541
542
  if (props.formatPrice) return props.formatPrice(price);
542
- return formatPrice(price || 0, { symbol: props.currency ?? "\u20AC" });
543
+ return formatPrice(price || 0, { symbol: props.currency ?? "\u20AC", locale: localeForLanguage(props.language) });
543
544
  }
544
545
  const subtotal = total?.gross || 0;
545
546
  const hasDiscount = !!total && total.discountType !== OrderDiscountType.N && total.discountValue > 0;
@@ -667,7 +668,7 @@ function ProductBulkPrices(props) {
667
668
  function getPrice(tier) {
668
669
  const value = includeTax ? tier.net : tier.gross;
669
670
  if (value === null || value === void 0) return "";
670
- return formatPrice(value, { symbol: props.currency ?? "\u20AC" });
671
+ return formatPrice(value, { symbol: props.currency ?? "\u20AC", locale: localeForLanguage(props.language) });
671
672
  }
672
673
  function getQuantityLabel(tier, index) {
673
674
  const discount = tier.discount;
@@ -678,13 +679,14 @@ function ProductBulkPrices(props) {
678
679
  if (nextQty) return `${qty}\u2013${nextQty - 1}`;
679
680
  return `${qty}+`;
680
681
  }
682
+ const title = props.labels?.title ?? "Volume pricing";
681
683
  return /* @__PURE__ */ jsx(Fragment, { children: !isHidden && hasItems ? /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(
682
684
  "div",
683
685
  {
684
686
  className: cn(`propeller-product-bulk-prices ${props.className || ""}`),
685
687
  "data-include-tax": includeTax ? "true" : "false",
686
688
  children: [
687
- getLabel(props.labels, "title", "Volume pricing") ? /* @__PURE__ */ jsx("h3", { className: "propeller-product-bulk-prices__title text-base font-semibold text-foreground mb-3", children: getLabel(props.labels, "title", "Volume pricing") }) : null,
689
+ title ? /* @__PURE__ */ jsx("h3", { className: "propeller-product-bulk-prices__title text-base font-semibold text-foreground mb-3", children: title }) : null,
688
690
  /* @__PURE__ */ jsx("div", { className: "propeller-product-bulk-prices__table-wrapper overflow-hidden rounded-container border border-border", children: /* @__PURE__ */ jsxs("table", { className: "propeller-product-bulk-prices__table w-full text-sm", children: [
689
691
  /* @__PURE__ */ jsx("thead", { className: "propeller-product-bulk-prices__thead bg-muted/50", children: /* @__PURE__ */ jsxs("tr", { children: [
690
692
  /* @__PURE__ */ jsx("th", { className: "propeller-product-bulk-prices__th propeller-product-bulk-prices__th--quantity px-4 py-2 text-left font-medium text-muted-foreground", children: getLabel(props.labels, "quantityFrom", "Qty from") }),
@@ -781,7 +783,7 @@ function ProductPriceDisplay(props) {
781
783
  }
782
784
  function formatPrice5(value) {
783
785
  if (value === null || value === void 0) return "";
784
- return formatPrice(value, { symbol: props.currency ?? "\u20AC" });
786
+ return formatPrice(value, { symbol: props.currency ?? "\u20AC", locale: localeForLanguage(props.language) });
785
787
  }
786
788
  function getOptionsTotal(useNet) {
787
789
  const options = props.options || [];