@propeller-commerce/propeller-v2-react-ui 0.20.1 → 0.22.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/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  "use client";
2
- import { getLabel, getLocalizedValue, formatPrice, localeForLanguage, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, isCheckoutAllowed, createServices, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, findPurchaserPac, isOverAuthorizationLimit, MIN_STOCK_THRESHOLD, getNettedBonusItems, getLanguageString, collectAttributeValues, getCountryName } from '@propeller-commerce/propeller-v2-core-ui';
2
+ import { getLabel, getLocalizedValue, formatPrice, localeForLanguage, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, isCheckoutAllowed, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, findPurchaserPac, isOverAuthorizationLimit, MIN_STOCK_THRESHOLD, getNettedBonusItems, getLanguageString, collectAttributeValues, getCountryName, resolveLanguageEntry, getLanguageUri } from '@propeller-commerce/propeller-v2-core-ui';
3
3
  export * from '@propeller-commerce/propeller-v2-core-ui';
4
4
  import * as React6 from 'react';
5
- import { createContext, useState, useRef, useCallback, useMemo, useContext, useEffect } from 'react';
5
+ import { createContext, useState, useRef, useCallback, useEffect, useMemo, useContext } from 'react';
6
6
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
7
  import { ProductStatus, ProductSortField, SortOrder, CrossupsellType, CartStatus, CartAddressType, Gender, YesNo, AddressType, MagicTokenService, PaymentStatuses, OrderSearchFields, OrderItemClass, ProductSearchableField, machineService, PurchaseRole, AttributeType, OrderSortField, OrderType, BundleCondition, OrderDiscountType } from '@propeller-commerce/propeller-sdk-v2';
8
8
  import { clsx } from 'clsx';
@@ -747,10 +747,29 @@ function useCart(options) {
747
747
  const [loading, setLoading] = useState(false);
748
748
  const [error, setError] = useState(null);
749
749
  const notesTimers = useRef({});
750
- const checkoutAllowed = useMemo(
751
- () => isCheckoutAllowed(user, companyId, cart),
752
- [user, companyId, cart]
753
- );
750
+ useEffect(() => {
751
+ if (!graphqlClient || !cartId || cart) return;
752
+ let cancelled = false;
753
+ (async () => {
754
+ try {
755
+ const fetched = await createServices(graphqlClient).cart.getCart({
756
+ cartId,
757
+ imageSearchFilters: imageSearchFilters(),
758
+ imageVariantFilters: imageVariantFilters(),
759
+ language
760
+ });
761
+ if (!cancelled && fetched) setCart(fetched);
762
+ } catch {
763
+ }
764
+ })();
765
+ return () => {
766
+ cancelled = true;
767
+ };
768
+ }, [graphqlClient, cartId, cart, language, imageSearchFilters, imageVariantFilters]);
769
+ const checkoutAllowed = useMemo(() => {
770
+ if (cartId && !cart) return false;
771
+ return isCheckoutAllowed(user, resolvedCompanyId, cart);
772
+ }, [user, resolvedCompanyId, cart, cartId]);
754
773
  function getMinQuantity(product) {
755
774
  const min = product?.minimumQuantity;
756
775
  return min && min > 0 ? min : 1;
@@ -1571,7 +1590,8 @@ var INFRA_KEYS = [
1571
1590
  "includeTax",
1572
1591
  "currency",
1573
1592
  "configuration",
1574
- "portalMode"
1593
+ "portalMode",
1594
+ "isAuthenticated"
1575
1595
  ];
1576
1596
  function useInfraProps(props) {
1577
1597
  const ctx = usePropellerContext();
@@ -2248,10 +2268,11 @@ function useProductSearch(options) {
2248
2268
  const isLoading = !isControlled && internalLoading;
2249
2269
  function filterByLanguage(products, lang) {
2250
2270
  if (!lang) return products;
2271
+ const target = lang.toUpperCase();
2251
2272
  return products.filter((p) => {
2252
2273
  const names = p.names || p.names || [];
2253
2274
  if (!names || names.length === 0) return true;
2254
- return names.some((n) => n.language === lang);
2275
+ return names.some((n) => (n.language || "").toUpperCase() === target);
2255
2276
  });
2256
2277
  }
2257
2278
  const fetchProducts = useCallback(async () => {
@@ -2756,7 +2777,8 @@ function useSpareParts(options) {
2756
2777
  const [childMachines, setChildMachines] = useState([]);
2757
2778
  const [itemsFound, setItemsFound] = useState(0);
2758
2779
  const [internalLoading, setInternalLoading] = useState(false);
2759
- const [currentPage, setCurrentPage] = useState(1);
2780
+ const [internalPage, setInternalPage] = useState(1);
2781
+ const currentPage = options.page ?? internalPage;
2760
2782
  const [totalPages, setTotalPages] = useState(1);
2761
2783
  const fetchIdRef = useRef(0);
2762
2784
  const displayParts = useMemo(
@@ -2905,7 +2927,7 @@ function useSpareParts(options) {
2905
2927
  userKey
2906
2928
  ]);
2907
2929
  const goToPage = useCallback((page) => {
2908
- setCurrentPage((prev) => {
2930
+ setInternalPage((prev) => {
2909
2931
  if (page >= 1 && (totalPages <= 1 || page <= totalPages)) return page;
2910
2932
  return prev;
2911
2933
  });
@@ -2924,9 +2946,9 @@ function useSpareParts(options) {
2924
2946
  var inflightFetches4 = /* @__PURE__ */ new Set();
2925
2947
  var ROOT_MACHINE_FIELDS = `
2926
2948
  id
2927
- name(language: $language) { language value }
2928
- description(language: $language) { language value }
2929
- slug(language: $language) { language value }
2949
+ name { language value }
2950
+ description { language value }
2951
+ slug { language value }
2930
2952
  media {
2931
2953
  images {
2932
2954
  items { imageVariants(input: $imageVariantFilters) { url } }
@@ -4207,7 +4229,7 @@ function AccountIconAndMenu(rawProps) {
4207
4229
  onClick: (event) => handleIconClick(),
4208
4230
  "aria-label": getLabel(props.labels, "accountLabel", "Account"),
4209
4231
  "data-open": menuOpen ? "true" : "false",
4210
- className: cn(`propeller-account-menu__trigger inline-flex items-center gap-2 px-3 py-2 rounded-control text-sm font-medium transition-colors text-foreground hover:bg-surface-hover${props.iconClassName ? " " + props.iconClassName : ""}`),
4232
+ className: cn(`propeller-account-menu__trigger inline-flex items-center gap-2 px-3 py-2 rounded-control text-sm font-medium transition-colors text-inherit${props.iconClassName ? " " + props.iconClassName : ""}`),
4211
4233
  children: [
4212
4234
  /* @__PURE__ */ jsx(
4213
4235
  "svg",
@@ -7619,7 +7641,8 @@ function CartIconAndSidebar(rawProps) {
7619
7641
  ] });
7620
7642
  }
7621
7643
  var CartIconAndSidebar_default = CartIconAndSidebar;
7622
- function CartOverview(props) {
7644
+ function CartOverview(rawProps) {
7645
+ const props = useInfraProps(rawProps);
7623
7646
  const [reference, setReference] = useState("");
7624
7647
  const [notes, setNotes] = useState("");
7625
7648
  const [termsAccepted, setTermsAccepted] = useState(false);
@@ -7629,6 +7652,7 @@ function CartOverview(props) {
7629
7652
  const showReference = props.showReference !== false;
7630
7653
  const showTermsAndConditions = props.showTermsAndConditions !== false;
7631
7654
  const showPurchaseButton = props.showPurchaseButton !== false;
7655
+ const overLimit = isOverAuthorizationLimit(props.user, props.companyId, props.cart);
7632
7656
  const invoiceAddress = props.cart?.invoiceAddress;
7633
7657
  const deliveryAddress = props.cart?.deliveryAddress;
7634
7658
  const getCountryName$1 = (code) => getCountryName(code, props.countries);
@@ -7768,7 +7792,12 @@ function CartOverview(props) {
7768
7792
  ] });
7769
7793
  })() })
7770
7794
  ] }) : null,
7771
- showPurchaseButton ? /* @__PURE__ */ jsxs(
7795
+ showPurchaseButton && overLimit ? /* @__PURE__ */ jsx("p", { className: "propeller-cart-overview__authorization-required text-sm text-muted-foreground mt-2", children: getLabel(
7796
+ props.labels,
7797
+ "authorizationRequired",
7798
+ "This order exceeds your authorization limit. Request authorization from the cart to continue."
7799
+ ) }) : null,
7800
+ showPurchaseButton && !overLimit ? /* @__PURE__ */ jsxs(
7772
7801
  "button",
7773
7802
  {
7774
7803
  type: "button",
@@ -8143,6 +8172,7 @@ var RESOLVE_SPEC = {
8143
8172
  currency: { infra: "currency", default: "\u20AC" },
8144
8173
  user: { infra: "user", default: null },
8145
8174
  portalMode: { infra: "portalMode" },
8175
+ isAuthenticated: { infra: "isAuthenticated" },
8146
8176
  columns: { grid: "columns", default: 3 },
8147
8177
  showPrice: { grid: "showPrice" },
8148
8178
  showStock: { grid: "showStock" },
@@ -8186,7 +8216,7 @@ function ClusterCard(rawProps) {
8186
8216
  const textLabelValues = resolveAttributeValues(cluster, props.textLabels).map((value) => ({
8187
8217
  value
8188
8218
  }));
8189
- const contentHidden = isContentHidden(props.portalMode, props.user);
8219
+ const contentHidden = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8190
8220
  const defaultProductInventory = contentHidden ? void 0 : cluster?.defaultProduct?.inventory;
8191
8221
  const useTax = props.includeTax !== void 0 ? !!props.includeTax : false;
8192
8222
  const priceObj = contentHidden ? void 0 : cluster?.defaultProduct?.price;
@@ -8529,6 +8559,7 @@ function getMachineImageUrl(machine) {
8529
8559
  }
8530
8560
  function MachineCard(props) {
8531
8561
  const { machine, href, language } = props;
8562
+ const Wrapper = href ? "a" : "div";
8532
8563
  const showImage = props.showImage ?? true;
8533
8564
  const showDescription = props.showDescription ?? false;
8534
8565
  const name = getLocalizedValue(machine.name, language);
@@ -8540,10 +8571,9 @@ function MachineCard(props) {
8540
8571
  {
8541
8572
  className: cn(`propeller-machine-card group relative flex h-full flex-col overflow-hidden rounded-lg border border-border bg-background ${props.className ?? ""}`),
8542
8573
  children: /* @__PURE__ */ jsxs(
8543
- "a",
8574
+ Wrapper,
8544
8575
  {
8545
- href,
8546
- onClick: handleClick,
8576
+ ...href ? { href, onClick: handleClick } : {},
8547
8577
  className: "flex h-full flex-col focus:outline-none focus-visible:ring-2 focus-visible:ring-primary",
8548
8578
  children: [
8549
8579
  showImage && /* @__PURE__ */ jsx("div", { className: "propeller-machine-card__image aspect-square w-full overflow-hidden bg-background-subtle", children: imageUrl ? /* @__PURE__ */ jsx(
@@ -8613,7 +8643,7 @@ function LoginToOrderButton(props) {
8613
8643
  var LoginToOrderButton_default = LoginToOrderButton;
8614
8644
  function ProductPriceDisplay(props) {
8615
8645
  function isHidden2() {
8616
- return isContentHidden(props.portalMode, props.user);
8646
+ return isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8617
8647
  }
8618
8648
  function formatPrice17(value) {
8619
8649
  if (value === null || value === void 0) return "";
@@ -8705,6 +8735,7 @@ var RESOLVE_SPEC2 = {
8705
8735
  language: { infra: "language", default: "NL" },
8706
8736
  includeTax: { infra: "includeTax" },
8707
8737
  portalMode: { infra: "portalMode" },
8738
+ isAuthenticated: { infra: "isAuthenticated" },
8708
8739
  configuration: { infra: "configuration" },
8709
8740
  // Same entry ClusterCard has always had. Without it a shop priced in
8710
8741
  // anything but euros had no route to the cards at all: the grid resolved the
@@ -8763,7 +8794,7 @@ function ProductCardRoot(rawProps) {
8763
8794
  const language = props.language || "NL";
8764
8795
  const product = props.product;
8765
8796
  const isRow = props.columns === 1;
8766
- const contentHidden = isContentHidden(props.portalMode, props.user);
8797
+ const contentHidden = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8767
8798
  const showStock = !!props.showStock && !contentHidden;
8768
8799
  const showCta = props.allowAddToCart !== false || contentHidden;
8769
8800
  const name = getProductName(product, language);
@@ -9060,6 +9091,7 @@ function ProductCardPrice(props) {
9060
9091
  currency: resolved.currency,
9061
9092
  language: resolved.language,
9062
9093
  portalMode: resolved.portalMode,
9094
+ isAuthenticated: resolved.isAuthenticated,
9063
9095
  user: resolved.user,
9064
9096
  showLoginPrompt: false
9065
9097
  }
@@ -9067,7 +9099,7 @@ function ProductCardPrice(props) {
9067
9099
  }
9068
9100
  function ProductCardAddToCart(props) {
9069
9101
  const { product, resolved } = useProductCard();
9070
- if (isContentHidden(resolved.portalMode, resolved.user)) {
9102
+ if (isContentHidden(resolved.portalMode, resolved.user, resolved.isAuthenticated)) {
9071
9103
  return /* @__PURE__ */ jsx(
9072
9104
  LoginToOrderButton_default,
9073
9105
  {
@@ -9088,9 +9120,13 @@ function ProductCardAddToCart(props) {
9088
9120
  allowIncrDecr: resolved.allowIncrDecr,
9089
9121
  showModal: resolved.showModal,
9090
9122
  enableStockValidation: resolved.enableStockValidation,
9123
+ onAddToCart: resolved.onAddToCart,
9091
9124
  afterAddToCart: resolved.afterAddToCart,
9092
9125
  onProceedToCheckout: resolved.onProceedToCheckout,
9093
9126
  onRequestQuoteClick: resolved.onRequestQuoteClick,
9127
+ createCart: resolved.createCart,
9128
+ onCartCreated: resolved.onCartCreated,
9129
+ includeTax: resolved.includeTax,
9094
9130
  labels: resolved.addToCartLabels
9095
9131
  }
9096
9132
  );
@@ -9213,7 +9249,7 @@ function ProductGrid(rawProps) {
9213
9249
  }
9214
9250
  function showAddToCart() {
9215
9251
  const allow = props.allowAddToCart !== false;
9216
- return !isContentHidden(props.portalMode, props.user) && allow;
9252
+ return !isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
9217
9253
  }
9218
9254
  function getSkeletonItems() {
9219
9255
  const cols = props.columns || 3;
@@ -9491,7 +9527,7 @@ function GridFilters(rawProps) {
9491
9527
  }
9492
9528
  function showAvailability() {
9493
9529
  if (!props.showAvailabilityFilter) return false;
9494
- return !isContentHidden(props.portalMode, props.user);
9530
+ return !isContentHidden(props.portalMode, props.user, props.isAuthenticated);
9495
9531
  }
9496
9532
  function getFilterName(filter) {
9497
9533
  return filter?.attributeDescription?.name || "";
@@ -10046,7 +10082,7 @@ function GridToolbar(rawProps) {
10046
10082
  return badges;
10047
10083
  }
10048
10084
  function isPriceSortDisabled() {
10049
- return isContentHidden(props.portalMode, props.user);
10085
+ return isContentHidden(props.portalMode, props.user, props.isAuthenticated);
10050
10086
  }
10051
10087
  function getAvailabilityLabel() {
10052
10088
  const qty = props.minStock;
@@ -10448,7 +10484,7 @@ function MachineGrid(rawProps) {
10448
10484
  // node query threads through useSpareParts.
10449
10485
  imageVariantFilters: configuration?.imageVariantFiltersMedium
10450
10486
  });
10451
- const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages, goToPage } = useSpareParts({
10487
+ const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages } = useSpareParts({
10452
10488
  graphqlClient: props.graphqlClient,
10453
10489
  // Idle at the root (no slug).
10454
10490
  slug: isRoot ? void 0 : currentSlug,
@@ -10464,6 +10500,10 @@ function MachineGrid(rawProps) {
10464
10500
  sortField,
10465
10501
  sortOrder,
10466
10502
  pageSize: offset,
10503
+ // The hook takes the controlled page directly now. It used to own its own
10504
+ // counter, so the only way to drive it from URL state was the effect
10505
+ // below — undiscoverable unless you read this file (PWP-995b).
10506
+ page: listing.page,
10467
10507
  configuration: rawProps.configuration ?? props.configuration,
10468
10508
  onFiltersChange: setGridFilters,
10469
10509
  onPriceBoundsChange: (min, max) => {
@@ -10473,9 +10513,6 @@ function MachineGrid(rawProps) {
10473
10513
  onItemsFoundChange: setItemsFound,
10474
10514
  onMachineChange: setMachine
10475
10515
  });
10476
- useEffect(() => {
10477
- goToPage(listing.page);
10478
- }, [listing.page, goToPage]);
10479
10516
  const machineName = machine ? getLocalizedValue(machine.name, machineLanguage) : slugToLabel(currentSlug);
10480
10517
  const partProducts = useMemo(
10481
10518
  () => displayParts.map((p) => p.product).filter(Boolean),
@@ -10532,11 +10569,10 @@ function MachineGrid(rawProps) {
10532
10569
  );
10533
10570
  const childSlugHref = (child) => {
10534
10571
  const slug = getLocalizedValue(child.slug, machineLanguage);
10535
- if (!slug) return null;
10536
- return `${currentPath}/${slug}`;
10572
+ return slug ? `${currentPath}/${slug}` : void 0;
10537
10573
  };
10538
10574
  if (isRoot) {
10539
- const cards = rootMachines.map((m) => ({ machine: m, href: childSlugHref(m) })).filter((c) => c.href !== null);
10575
+ const cards = rootMachines.map((m) => ({ machine: m, href: childSlugHref(m) }));
10540
10576
  return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
10541
10577
  /* @__PURE__ */ jsx("h1", { className: "propeller-grid-title mb-6 text-2xl font-semibold", children: rawProps.rootTitle ?? "Machines" }),
10542
10578
  rootLoading ? /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: getLabel(rawProps.machineCardLabels, "loading", "Loading\u2026") }) : cards.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: getLabel(rawProps.machineCardLabels, "noMachines", "No machines found.") }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: cards.map(({ machine: m, href }) => /* @__PURE__ */ jsx(
@@ -10568,7 +10604,6 @@ function MachineGrid(rawProps) {
10568
10604
  /* @__PURE__ */ jsx("h1", { className: "propeller-grid-title mb-6 text-2xl font-semibold", children: machineName }),
10569
10605
  childMachines.length > 0 && /* @__PURE__ */ jsx("div", { className: "propeller-machine-children mb-8 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: childMachines.map((child) => {
10570
10606
  const href = childSlugHref(child);
10571
- if (!href) return null;
10572
10607
  return /* @__PURE__ */ jsx(
10573
10608
  MachineCard,
10574
10609
  {
@@ -10646,6 +10681,12 @@ function MachineGrid(rawProps) {
10646
10681
  columns: viewMode === "list" ? 1 : 3,
10647
10682
  showAvailability: rawProps.showAvailability ?? false,
10648
10683
  showStock: rawProps.showStock ?? true,
10684
+ productCardComponent: rawProps.productCardComponent,
10685
+ productCardLabels: rawProps.productCardLabels,
10686
+ addToCartLabels: rawProps.addToCartLabels,
10687
+ stockLabels: rawProps.stockLabels,
10688
+ priceLabels: rawProps.priceLabels,
10689
+ labels: rawProps.labels,
10649
10690
  belowName: (product) => {
10650
10691
  const qty = quantityBySku.get(product.sku);
10651
10692
  if (!qty) return null;
@@ -10894,7 +10935,7 @@ function ClusterOptions(rawProps) {
10894
10935
  const language = props.language;
10895
10936
  const options = props.options || [];
10896
10937
  const sel = selectedProductIds;
10897
- const hidePrices = isContentHidden(props.portalMode, props.user);
10938
+ const hidePrices = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
10898
10939
  return options.filter((option) => option.hidden !== YesNo.Y).map((option) => {
10899
10940
  const idStr = option.id.toString();
10900
10941
  const selectedProductId = sel[idStr] || "";
@@ -11078,7 +11119,11 @@ function CompanySwitcher(props) {
11078
11119
  type: "button",
11079
11120
  "aria-haspopup": "listbox",
11080
11121
  "aria-label": getLabel(props.labels, "switchCompanyAriaLabel", "Switch company"),
11081
- className: "propeller-company-switcher__trigger flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium transition-colors hover:bg-white/10",
11122
+ className: cn(
11123
+ // `text-inherit`: this sits in the host's header, which may be dark or
11124
+ // light. See the note on AccountIconAndMenu's trigger.
11125
+ `propeller-company-switcher__trigger flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium transition-colors text-inherit${props.triggerClassName ? " " + props.triggerClassName : ""}`
11126
+ ),
11082
11127
  onClick: (event) => toggleDropdown(),
11083
11128
  "aria-expanded": isOpen,
11084
11129
  children: [
@@ -15053,7 +15098,7 @@ function PriceToggle(props) {
15053
15098
  var PriceToggle_default = PriceToggle;
15054
15099
  function ProductBulkPrices(props) {
15055
15100
  const includeTax = !!props.includeTax;
15056
- const isHidden2 = isContentHidden(props.portalMode, props.user);
15101
+ const isHidden2 = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
15057
15102
  function getTierQuantity(tier) {
15058
15103
  const discount = tier.discount;
15059
15104
  return discount?.quantityFrom ?? tier.quantity ?? null;
@@ -15600,14 +15645,10 @@ function ProductDownloads(props) {
15600
15645
  const hasItems = items.length > 0;
15601
15646
  const lang = props.language || "NL";
15602
15647
  function getDocumentUrl(doc) {
15603
- const docs = doc.documents || [];
15604
- const match = docs.find((d) => d.language === lang);
15605
- return match?.originalUrl || docs?.[0]?.originalUrl || "";
15648
+ return resolveLanguageEntry(doc.documents, lang)?.originalUrl || "";
15606
15649
  }
15607
15650
  function getDocumentName(doc) {
15608
- const alts = doc.alt || [];
15609
- const match = alts.find((a) => a.language === lang);
15610
- return match?.value || alts?.[0]?.value || "Download";
15651
+ return getLanguageString(doc.alt, lang, "Download");
15611
15652
  }
15612
15653
  return /* @__PURE__ */ jsxs("div", { className: cn(`propeller-product-downloads ${props.className || ""}`), children: [
15613
15654
  hasItems ? /* @__PURE__ */ jsx("h3", { className: "propeller-product-downloads__title text-base font-semibold text-foreground mb-3", children: getLabel(props.labels, "title", "Downloads") }) : null,
@@ -15813,12 +15854,14 @@ function ProductGallery(props) {
15813
15854
  var ProductGallery_default = ProductGallery;
15814
15855
  function pickImageUrl(mediaItems, target) {
15815
15856
  if (!mediaItems || mediaItems.length === 0) return null;
15857
+ const targetUpper = (target || "").toUpperCase();
15858
+ const langMatches = (value) => (value || "").toUpperCase() === targetUpper;
15816
15859
  for (const m of mediaItems) {
15817
- const match = m.imageVariants?.find((v) => v.language === target && v.url);
15860
+ const match = m.imageVariants?.find((v) => langMatches(v.language) && v.url);
15818
15861
  if (match?.url) return match.url;
15819
15862
  }
15820
15863
  for (const m of mediaItems) {
15821
- const match = m.images?.find((i) => i.language === target && i.originalUrl);
15864
+ const match = m.images?.find((i) => langMatches(i.language) && i.originalUrl);
15822
15865
  if (match?.originalUrl) return match.originalUrl;
15823
15866
  }
15824
15867
  for (const m of mediaItems) {
@@ -16146,6 +16189,7 @@ function ProductInfoPrice(props = {}) {
16146
16189
  currency: derived.currency,
16147
16190
  taxZone: derived.taxZone,
16148
16191
  portalMode: derived.portalMode,
16192
+ isAuthenticated: resolved.isAuthenticated,
16149
16193
  user: derived.user,
16150
16194
  labels: derived.labels,
16151
16195
  className: props.className
@@ -16155,7 +16199,7 @@ function ProductInfoPrice(props = {}) {
16155
16199
  function ProductInfoStock(props = {}) {
16156
16200
  const { product, resolved, derived } = useProductInfoContext();
16157
16201
  if (!product.inventory) return null;
16158
- if (isContentHidden(derived.portalMode, resolved.user)) return null;
16202
+ if (isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) return null;
16159
16203
  const Injected = resolved.stockComponent;
16160
16204
  if (Injected) {
16161
16205
  return /* @__PURE__ */ jsx(
@@ -16182,7 +16226,7 @@ function ProductInfoStock(props = {}) {
16182
16226
  }
16183
16227
  function ProductInfoAddToCart(props = {}) {
16184
16228
  const { product, resolved, derived } = useProductInfoContext();
16185
- if (isContentHidden(derived.portalMode, resolved.user)) {
16229
+ if (isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) {
16186
16230
  return /* @__PURE__ */ jsx(
16187
16231
  LoginToOrderButton_default,
16188
16232
  {
@@ -16194,7 +16238,18 @@ function ProductInfoAddToCart(props = {}) {
16194
16238
  }
16195
16239
  const Injected = resolved.addToCartComponent;
16196
16240
  if (Injected) {
16197
- return /* @__PURE__ */ jsx(Injected, { product, labels: derived.labels, className: props.className });
16241
+ return /* @__PURE__ */ jsx(
16242
+ Injected,
16243
+ {
16244
+ product,
16245
+ cartId: resolved.cartId,
16246
+ createCart: resolved.createCart,
16247
+ onCartCreated: resolved.onCartCreated,
16248
+ includeTax: derived.useTax,
16249
+ labels: derived.labels,
16250
+ className: props.className
16251
+ }
16252
+ );
16198
16253
  }
16199
16254
  return /* @__PURE__ */ jsx(
16200
16255
  AddToCart_default,
@@ -16203,6 +16258,9 @@ function ProductInfoAddToCart(props = {}) {
16203
16258
  user: derived.user,
16204
16259
  currency: derived.currency,
16205
16260
  product,
16261
+ cartId: resolved.cartId,
16262
+ createCart: resolved.createCart,
16263
+ onCartCreated: resolved.onCartCreated,
16206
16264
  configuration: resolved.configuration,
16207
16265
  companyId: resolved.companyId,
16208
16266
  includeTax: derived.useTax,
@@ -16256,6 +16314,7 @@ function ProductInfoBulkPrices(props = {}) {
16256
16314
  bulkPrices: product.bulkPrices,
16257
16315
  includeTax: derived.useTax,
16258
16316
  portalMode: derived.portalMode,
16317
+ isAuthenticated: resolved.isAuthenticated,
16259
16318
  user: derived.user,
16260
16319
  taxZone: derived.taxZone,
16261
16320
  currency: derived.currency,
@@ -16418,7 +16477,7 @@ function ProductSlider(rawProps) {
16418
16477
  }
16419
16478
  function showAddToCart() {
16420
16479
  const allow = props.allowAddToCart !== false;
16421
- return !isContentHidden(props.portalMode, props.user) && allow;
16480
+ return !isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
16422
16481
  }
16423
16482
  function isCluster(item) {
16424
16483
  return "clusterId" in item && !("productId" in item);
@@ -16722,14 +16781,10 @@ function ProductVideos(props) {
16722
16781
  const hasItems = items.length > 0;
16723
16782
  const lang = props.language || "NL";
16724
16783
  function getVideoUri(video) {
16725
- const vids = video.videos || [];
16726
- const match = vids.find((v) => v.language === lang);
16727
- return match?.uri || vids?.[0]?.uri || "";
16784
+ return getLanguageUri(video.videos, lang, "");
16728
16785
  }
16729
16786
  function getVideoTitle(video) {
16730
- const alts = video.alt || [];
16731
- const match = alts.find((a) => a.language === lang);
16732
- return match?.value || alts?.[0]?.value || "Video";
16787
+ return getLanguageString(video.alt, lang, "Video");
16733
16788
  }
16734
16789
  function isEmbeddable(uri) {
16735
16790
  return uri.includes("youtube.com") || uri.includes("youtu.be") || uri.includes("vimeo.com");