@propeller-commerce/propeller-v2-react-ui 0.21.0 → 0.23.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 () => {
@@ -2741,6 +2762,40 @@ function useQuickOrder(options) {
2741
2762
  );
2742
2763
  return { submitting, error, searchProducts, submit };
2743
2764
  }
2765
+
2766
+ // src/composables/shared/utils/machineLanguage.ts
2767
+ var MACHINE_NOT_FOUND = "SPARE_PARTS_MACHINE_NOT_FOUND_ERROR";
2768
+ function isMachineNotFound(error) {
2769
+ const entries = error?.errors;
2770
+ if (!Array.isArray(entries)) return false;
2771
+ return entries.some(
2772
+ (e) => e?.extensions?.code === MACHINE_NOT_FOUND || /no machine found for slug and language/i.test(e?.message ?? "")
2773
+ );
2774
+ }
2775
+ function machineLanguageCandidates(machineLanguage, language, extra) {
2776
+ const ordered = [machineLanguage, language, ...extra ?? []];
2777
+ const seen = /* @__PURE__ */ new Set();
2778
+ const out = [];
2779
+ for (const raw of ordered) {
2780
+ const value = raw?.trim().toUpperCase();
2781
+ if (!value || seen.has(value)) continue;
2782
+ seen.add(value);
2783
+ out.push(value);
2784
+ }
2785
+ return out;
2786
+ }
2787
+ async function resolveMachineAcrossLanguages(candidates, fetchOne) {
2788
+ for (const language of candidates) {
2789
+ try {
2790
+ return { machine: await fetchOne(language), language };
2791
+ } catch (error) {
2792
+ if (!isMachineNotFound(error)) throw error;
2793
+ }
2794
+ }
2795
+ return null;
2796
+ }
2797
+
2798
+ // src/composables/react/useSpareParts.ts
2744
2799
  var inflightFetches3 = /* @__PURE__ */ new Set();
2745
2800
  var STOREFRONT_STATUSES = [
2746
2801
  ProductStatus.A,
@@ -2757,6 +2812,8 @@ function useSpareParts(options) {
2757
2812
  const [itemsFound, setItemsFound] = useState(0);
2758
2813
  const [internalLoading, setInternalLoading] = useState(false);
2759
2814
  const [internalPage, setInternalPage] = useState(1);
2815
+ const [notFound, setNotFound] = useState(false);
2816
+ const machineLanguagesKey = (options.machineLanguages ?? []).join(",");
2760
2817
  const currentPage = options.page ?? internalPage;
2761
2818
  const [totalPages, setTotalPages] = useState(1);
2762
2819
  const fetchIdRef = useRef(0);
@@ -2822,17 +2879,35 @@ function useSpareParts(options) {
2822
2879
  const filterAvailableAttributeInput = {
2823
2880
  isSearchable: true
2824
2881
  };
2825
- const machine = await service.getMachine({
2826
- slug: options.slug,
2827
- // The machine tree's language, NOT the parts' — see `machineLanguage`.
2828
- language: options.machineLanguage ?? options.language,
2829
- sparePartsMachineProductSearchInput,
2830
- filterAvailableAttributeInput,
2831
- priceCalculateProductInput,
2832
- imageSearchFilters: options.configuration?.imageSearchFiltersGrid,
2833
- imageVariantFilters: options.configuration?.imageVariantFiltersMedium
2834
- });
2882
+ const resolved = await resolveMachineAcrossLanguages(
2883
+ machineLanguageCandidates(
2884
+ options.machineLanguage,
2885
+ options.language,
2886
+ options.machineLanguages
2887
+ ),
2888
+ (language) => service.getMachine({
2889
+ slug: options.slug,
2890
+ // The machine tree's language, NOT the parts' — see `machineLanguage`.
2891
+ language,
2892
+ sparePartsMachineProductSearchInput,
2893
+ filterAvailableAttributeInput,
2894
+ priceCalculateProductInput,
2895
+ imageSearchFilters: options.configuration?.imageSearchFiltersGrid,
2896
+ imageVariantFilters: options.configuration?.imageVariantFiltersMedium
2897
+ })
2898
+ );
2835
2899
  if (thisId !== fetchIdRef.current) return;
2900
+ if (!resolved) {
2901
+ setNotFound(true);
2902
+ setInternalParts([]);
2903
+ setChildMachines([]);
2904
+ setItemsFound(0);
2905
+ options.onItemsFoundChange?.(0);
2906
+ setTotalPages(1);
2907
+ return;
2908
+ }
2909
+ setNotFound(false);
2910
+ const machine = resolved.machine;
2836
2911
  const partsResponse = machine?.sparePartProducts;
2837
2912
  const items = partsResponse?.items ?? [];
2838
2913
  setInternalParts(items);
@@ -2874,6 +2949,7 @@ function useSpareParts(options) {
2874
2949
  options.term,
2875
2950
  options.language,
2876
2951
  options.machineLanguage,
2952
+ machineLanguagesKey,
2877
2953
  options.taxZone,
2878
2954
  options.companyId,
2879
2955
  options.textFilters,
@@ -2894,6 +2970,7 @@ function useSpareParts(options) {
2894
2970
  options.term,
2895
2971
  options.language,
2896
2972
  options.machineLanguage,
2973
+ machineLanguagesKey,
2897
2974
  options.companyId,
2898
2975
  // eslint-disable-next-line react-hooks/exhaustive-deps
2899
2976
  JSON.stringify(options.textFilters ?? []),
@@ -2919,7 +2996,8 @@ function useSpareParts(options) {
2919
2996
  currentPage,
2920
2997
  totalPages,
2921
2998
  fetchParts,
2922
- goToPage
2999
+ goToPage,
3000
+ notFound: !isControlled && notFound
2923
3001
  };
2924
3002
  }
2925
3003
  var inflightFetches4 = /* @__PURE__ */ new Set();
@@ -4208,7 +4286,7 @@ function AccountIconAndMenu(rawProps) {
4208
4286
  onClick: (event) => handleIconClick(),
4209
4287
  "aria-label": getLabel(props.labels, "accountLabel", "Account"),
4210
4288
  "data-open": menuOpen ? "true" : "false",
4211
- 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 : ""}`),
4289
+ 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 : ""}`),
4212
4290
  children: [
4213
4291
  /* @__PURE__ */ jsx(
4214
4292
  "svg",
@@ -7620,7 +7698,8 @@ function CartIconAndSidebar(rawProps) {
7620
7698
  ] });
7621
7699
  }
7622
7700
  var CartIconAndSidebar_default = CartIconAndSidebar;
7623
- function CartOverview(props) {
7701
+ function CartOverview(rawProps) {
7702
+ const props = useInfraProps(rawProps);
7624
7703
  const [reference, setReference] = useState("");
7625
7704
  const [notes, setNotes] = useState("");
7626
7705
  const [termsAccepted, setTermsAccepted] = useState(false);
@@ -7630,6 +7709,7 @@ function CartOverview(props) {
7630
7709
  const showReference = props.showReference !== false;
7631
7710
  const showTermsAndConditions = props.showTermsAndConditions !== false;
7632
7711
  const showPurchaseButton = props.showPurchaseButton !== false;
7712
+ const overLimit = isOverAuthorizationLimit(props.user, props.companyId, props.cart);
7633
7713
  const invoiceAddress = props.cart?.invoiceAddress;
7634
7714
  const deliveryAddress = props.cart?.deliveryAddress;
7635
7715
  const getCountryName$1 = (code) => getCountryName(code, props.countries);
@@ -7769,7 +7849,12 @@ function CartOverview(props) {
7769
7849
  ] });
7770
7850
  })() })
7771
7851
  ] }) : null,
7772
- showPurchaseButton ? /* @__PURE__ */ jsxs(
7852
+ showPurchaseButton && overLimit ? /* @__PURE__ */ jsx("p", { className: "propeller-cart-overview__authorization-required text-sm text-muted-foreground mt-2", children: getLabel(
7853
+ props.labels,
7854
+ "authorizationRequired",
7855
+ "This order exceeds your authorization limit. Request authorization from the cart to continue."
7856
+ ) }) : null,
7857
+ showPurchaseButton && !overLimit ? /* @__PURE__ */ jsxs(
7773
7858
  "button",
7774
7859
  {
7775
7860
  type: "button",
@@ -8144,6 +8229,7 @@ var RESOLVE_SPEC = {
8144
8229
  currency: { infra: "currency", default: "\u20AC" },
8145
8230
  user: { infra: "user", default: null },
8146
8231
  portalMode: { infra: "portalMode" },
8232
+ isAuthenticated: { infra: "isAuthenticated" },
8147
8233
  columns: { grid: "columns", default: 3 },
8148
8234
  showPrice: { grid: "showPrice" },
8149
8235
  showStock: { grid: "showStock" },
@@ -8187,7 +8273,7 @@ function ClusterCard(rawProps) {
8187
8273
  const textLabelValues = resolveAttributeValues(cluster, props.textLabels).map((value) => ({
8188
8274
  value
8189
8275
  }));
8190
- const contentHidden = isContentHidden(props.portalMode, props.user);
8276
+ const contentHidden = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8191
8277
  const defaultProductInventory = contentHidden ? void 0 : cluster?.defaultProduct?.inventory;
8192
8278
  const useTax = props.includeTax !== void 0 ? !!props.includeTax : false;
8193
8279
  const priceObj = contentHidden ? void 0 : cluster?.defaultProduct?.price;
@@ -8614,7 +8700,7 @@ function LoginToOrderButton(props) {
8614
8700
  var LoginToOrderButton_default = LoginToOrderButton;
8615
8701
  function ProductPriceDisplay(props) {
8616
8702
  function isHidden2() {
8617
- return isContentHidden(props.portalMode, props.user);
8703
+ return isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8618
8704
  }
8619
8705
  function formatPrice17(value) {
8620
8706
  if (value === null || value === void 0) return "";
@@ -8706,6 +8792,7 @@ var RESOLVE_SPEC2 = {
8706
8792
  language: { infra: "language", default: "NL" },
8707
8793
  includeTax: { infra: "includeTax" },
8708
8794
  portalMode: { infra: "portalMode" },
8795
+ isAuthenticated: { infra: "isAuthenticated" },
8709
8796
  configuration: { infra: "configuration" },
8710
8797
  // Same entry ClusterCard has always had. Without it a shop priced in
8711
8798
  // anything but euros had no route to the cards at all: the grid resolved the
@@ -8764,7 +8851,7 @@ function ProductCardRoot(rawProps) {
8764
8851
  const language = props.language || "NL";
8765
8852
  const product = props.product;
8766
8853
  const isRow = props.columns === 1;
8767
- const contentHidden = isContentHidden(props.portalMode, props.user);
8854
+ const contentHidden = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8768
8855
  const showStock = !!props.showStock && !contentHidden;
8769
8856
  const showCta = props.allowAddToCart !== false || contentHidden;
8770
8857
  const name = getProductName(product, language);
@@ -9061,6 +9148,7 @@ function ProductCardPrice(props) {
9061
9148
  currency: resolved.currency,
9062
9149
  language: resolved.language,
9063
9150
  portalMode: resolved.portalMode,
9151
+ isAuthenticated: resolved.isAuthenticated,
9064
9152
  user: resolved.user,
9065
9153
  showLoginPrompt: false
9066
9154
  }
@@ -9068,7 +9156,7 @@ function ProductCardPrice(props) {
9068
9156
  }
9069
9157
  function ProductCardAddToCart(props) {
9070
9158
  const { product, resolved } = useProductCard();
9071
- if (isContentHidden(resolved.portalMode, resolved.user)) {
9159
+ if (isContentHidden(resolved.portalMode, resolved.user, resolved.isAuthenticated)) {
9072
9160
  return /* @__PURE__ */ jsx(
9073
9161
  LoginToOrderButton_default,
9074
9162
  {
@@ -9089,9 +9177,13 @@ function ProductCardAddToCart(props) {
9089
9177
  allowIncrDecr: resolved.allowIncrDecr,
9090
9178
  showModal: resolved.showModal,
9091
9179
  enableStockValidation: resolved.enableStockValidation,
9180
+ onAddToCart: resolved.onAddToCart,
9092
9181
  afterAddToCart: resolved.afterAddToCart,
9093
9182
  onProceedToCheckout: resolved.onProceedToCheckout,
9094
9183
  onRequestQuoteClick: resolved.onRequestQuoteClick,
9184
+ createCart: resolved.createCart,
9185
+ onCartCreated: resolved.onCartCreated,
9186
+ includeTax: resolved.includeTax,
9095
9187
  labels: resolved.addToCartLabels
9096
9188
  }
9097
9189
  );
@@ -9214,7 +9306,7 @@ function ProductGrid(rawProps) {
9214
9306
  }
9215
9307
  function showAddToCart() {
9216
9308
  const allow = props.allowAddToCart !== false;
9217
- return !isContentHidden(props.portalMode, props.user) && allow;
9309
+ return !isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
9218
9310
  }
9219
9311
  function getSkeletonItems() {
9220
9312
  const cols = props.columns || 3;
@@ -9492,7 +9584,7 @@ function GridFilters(rawProps) {
9492
9584
  }
9493
9585
  function showAvailability() {
9494
9586
  if (!props.showAvailabilityFilter) return false;
9495
- return !isContentHidden(props.portalMode, props.user);
9587
+ return !isContentHidden(props.portalMode, props.user, props.isAuthenticated);
9496
9588
  }
9497
9589
  function getFilterName(filter) {
9498
9590
  return filter?.attributeDescription?.name || "";
@@ -10047,7 +10139,7 @@ function GridToolbar(rawProps) {
10047
10139
  return badges;
10048
10140
  }
10049
10141
  function isPriceSortDisabled() {
10050
- return isContentHidden(props.portalMode, props.user);
10142
+ return isContentHidden(props.portalMode, props.user, props.isAuthenticated);
10051
10143
  }
10052
10144
  function getAvailabilityLabel() {
10053
10145
  const qty = props.minStock;
@@ -10449,13 +10541,14 @@ function MachineGrid(rawProps) {
10449
10541
  // node query threads through useSpareParts.
10450
10542
  imageVariantFilters: configuration?.imageVariantFiltersMedium
10451
10543
  });
10452
- const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages } = useSpareParts({
10544
+ const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages, notFound } = useSpareParts({
10453
10545
  graphqlClient: props.graphqlClient,
10454
10546
  // Idle at the root (no slug).
10455
10547
  slug: isRoot ? void 0 : currentSlug,
10456
10548
  term: term || void 0,
10457
10549
  language,
10458
10550
  machineLanguage,
10551
+ machineLanguages: rawProps.machineLanguages,
10459
10552
  taxZone: props.taxZone,
10460
10553
  user: props.user,
10461
10554
  companyId: props.companyId,
@@ -10479,6 +10572,7 @@ function MachineGrid(rawProps) {
10479
10572
  onMachineChange: setMachine
10480
10573
  });
10481
10574
  const machineName = machine ? getLocalizedValue(machine.name, machineLanguage) : slugToLabel(currentSlug);
10575
+ const machineLabel = (key, fallback) => rawProps.machineCardLabels?.[key] ?? rawProps.toolbarLabels?.[key] ?? fallback;
10482
10576
  const partProducts = useMemo(
10483
10577
  () => displayParts.map((p) => p.product).filter(Boolean),
10484
10578
  [displayParts]
@@ -10553,6 +10647,12 @@ function MachineGrid(rawProps) {
10553
10647
  ] });
10554
10648
  }
10555
10649
  const hasParts = itemsFound > 0 || partProducts.length > 0;
10650
+ if (notFound && !partsLoading) {
10651
+ return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
10652
+ /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "propeller-breadcrumbs mb-6", children: /* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-2 text-sm text-foreground-subtle", children: /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: basePath, className: "hover:text-primary", children: rawProps.rootTitle ?? "Machines" }) }) }) }),
10653
+ /* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: machineLabel("machineNotFound", "This machine could not be found.") })
10654
+ ] });
10655
+ }
10556
10656
  return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
10557
10657
  /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "propeller-breadcrumbs mb-6", children: /* @__PURE__ */ jsxs("ol", { className: "flex flex-wrap items-center gap-2 text-sm text-foreground-subtle", children: [
10558
10658
  /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: basePath, className: "hover:text-primary", children: rawProps.rootTitle ?? "Machines" }) }),
@@ -10605,7 +10705,9 @@ function MachineGrid(rawProps) {
10605
10705
  {
10606
10706
  defaultValue: term,
10607
10707
  onSubmit: submitSearch,
10608
- labels: rawProps.toolbarLabels
10708
+ searchLabel: machineLabel("searchParts", "Search parts\u2026"),
10709
+ searchAriaLabel: machineLabel("searchParts", "Search parts"),
10710
+ buttonLabel: getLabel(rawProps.toolbarLabels, "search", "Search")
10609
10711
  },
10610
10712
  term
10611
10713
  ),
@@ -10656,7 +10758,7 @@ function MachineGrid(rawProps) {
10656
10758
  const qty = quantityBySku.get(product.sku);
10657
10759
  if (!qty) return null;
10658
10760
  return /* @__PURE__ */ jsxs("span", { className: "propeller-spare-part__quantity text-sm text-foreground-subtle", children: [
10659
- rawProps.toolbarLabels?.quantityInMachine ?? "Qty in machine",
10761
+ machineLabel("quantityInMachine", "Qty in machine"),
10660
10762
  ": ",
10661
10763
  qty
10662
10764
  ] });
@@ -10688,8 +10790,8 @@ function MachinePartsSearch(props) {
10688
10790
  onKeyDown: (e) => {
10689
10791
  if (e.key === "Enter") submit();
10690
10792
  },
10691
- placeholder: props.labels?.searchParts ?? "Search parts\u2026",
10692
- "aria-label": props.labels?.searchParts ?? "Search parts",
10793
+ placeholder: props.searchLabel,
10794
+ "aria-label": props.searchAriaLabel,
10693
10795
  className: "w-full rounded border border-border bg-background px-3 py-2"
10694
10796
  }
10695
10797
  ),
@@ -10699,7 +10801,7 @@ function MachinePartsSearch(props) {
10699
10801
  type: "button",
10700
10802
  onClick: submit,
10701
10803
  className: "rounded bg-primary px-4 py-2 text-primary-foreground",
10702
- children: props.labels?.search ?? "Search"
10804
+ children: props.buttonLabel
10703
10805
  }
10704
10806
  )
10705
10807
  ] });
@@ -10900,7 +11002,7 @@ function ClusterOptions(rawProps) {
10900
11002
  const language = props.language;
10901
11003
  const options = props.options || [];
10902
11004
  const sel = selectedProductIds;
10903
- const hidePrices = isContentHidden(props.portalMode, props.user);
11005
+ const hidePrices = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
10904
11006
  return options.filter((option) => option.hidden !== YesNo.Y).map((option) => {
10905
11007
  const idStr = option.id.toString();
10906
11008
  const selectedProductId = sel[idStr] || "";
@@ -11084,7 +11186,11 @@ function CompanySwitcher(props) {
11084
11186
  type: "button",
11085
11187
  "aria-haspopup": "listbox",
11086
11188
  "aria-label": getLabel(props.labels, "switchCompanyAriaLabel", "Switch company"),
11087
- 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",
11189
+ className: cn(
11190
+ // `text-inherit`: this sits in the host's header, which may be dark or
11191
+ // light. See the note on AccountIconAndMenu's trigger.
11192
+ `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 : ""}`
11193
+ ),
11088
11194
  onClick: (event) => toggleDropdown(),
11089
11195
  "aria-expanded": isOpen,
11090
11196
  children: [
@@ -15059,7 +15165,7 @@ function PriceToggle(props) {
15059
15165
  var PriceToggle_default = PriceToggle;
15060
15166
  function ProductBulkPrices(props) {
15061
15167
  const includeTax = !!props.includeTax;
15062
- const isHidden2 = isContentHidden(props.portalMode, props.user);
15168
+ const isHidden2 = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
15063
15169
  function getTierQuantity(tier) {
15064
15170
  const discount = tier.discount;
15065
15171
  return discount?.quantityFrom ?? tier.quantity ?? null;
@@ -15606,14 +15712,10 @@ function ProductDownloads(props) {
15606
15712
  const hasItems = items.length > 0;
15607
15713
  const lang = props.language || "NL";
15608
15714
  function getDocumentUrl(doc) {
15609
- const docs = doc.documents || [];
15610
- const match = docs.find((d) => d.language === lang);
15611
- return match?.originalUrl || docs?.[0]?.originalUrl || "";
15715
+ return resolveLanguageEntry(doc.documents, lang)?.originalUrl || "";
15612
15716
  }
15613
15717
  function getDocumentName(doc) {
15614
- const alts = doc.alt || [];
15615
- const match = alts.find((a) => a.language === lang);
15616
- return match?.value || alts?.[0]?.value || "Download";
15718
+ return getLanguageString(doc.alt, lang, "Download");
15617
15719
  }
15618
15720
  return /* @__PURE__ */ jsxs("div", { className: cn(`propeller-product-downloads ${props.className || ""}`), children: [
15619
15721
  hasItems ? /* @__PURE__ */ jsx("h3", { className: "propeller-product-downloads__title text-base font-semibold text-foreground mb-3", children: getLabel(props.labels, "title", "Downloads") }) : null,
@@ -15819,12 +15921,14 @@ function ProductGallery(props) {
15819
15921
  var ProductGallery_default = ProductGallery;
15820
15922
  function pickImageUrl(mediaItems, target) {
15821
15923
  if (!mediaItems || mediaItems.length === 0) return null;
15924
+ const targetUpper = (target || "").toUpperCase();
15925
+ const langMatches = (value) => (value || "").toUpperCase() === targetUpper;
15822
15926
  for (const m of mediaItems) {
15823
- const match = m.imageVariants?.find((v) => v.language === target && v.url);
15927
+ const match = m.imageVariants?.find((v) => langMatches(v.language) && v.url);
15824
15928
  if (match?.url) return match.url;
15825
15929
  }
15826
15930
  for (const m of mediaItems) {
15827
- const match = m.images?.find((i) => i.language === target && i.originalUrl);
15931
+ const match = m.images?.find((i) => langMatches(i.language) && i.originalUrl);
15828
15932
  if (match?.originalUrl) return match.originalUrl;
15829
15933
  }
15830
15934
  for (const m of mediaItems) {
@@ -16152,6 +16256,7 @@ function ProductInfoPrice(props = {}) {
16152
16256
  currency: derived.currency,
16153
16257
  taxZone: derived.taxZone,
16154
16258
  portalMode: derived.portalMode,
16259
+ isAuthenticated: resolved.isAuthenticated,
16155
16260
  user: derived.user,
16156
16261
  labels: derived.labels,
16157
16262
  className: props.className
@@ -16161,7 +16266,7 @@ function ProductInfoPrice(props = {}) {
16161
16266
  function ProductInfoStock(props = {}) {
16162
16267
  const { product, resolved, derived } = useProductInfoContext();
16163
16268
  if (!product.inventory) return null;
16164
- if (isContentHidden(derived.portalMode, resolved.user)) return null;
16269
+ if (isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) return null;
16165
16270
  const Injected = resolved.stockComponent;
16166
16271
  if (Injected) {
16167
16272
  return /* @__PURE__ */ jsx(
@@ -16188,7 +16293,7 @@ function ProductInfoStock(props = {}) {
16188
16293
  }
16189
16294
  function ProductInfoAddToCart(props = {}) {
16190
16295
  const { product, resolved, derived } = useProductInfoContext();
16191
- if (isContentHidden(derived.portalMode, resolved.user)) {
16296
+ if (isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) {
16192
16297
  return /* @__PURE__ */ jsx(
16193
16298
  LoginToOrderButton_default,
16194
16299
  {
@@ -16200,7 +16305,18 @@ function ProductInfoAddToCart(props = {}) {
16200
16305
  }
16201
16306
  const Injected = resolved.addToCartComponent;
16202
16307
  if (Injected) {
16203
- return /* @__PURE__ */ jsx(Injected, { product, labels: derived.labels, className: props.className });
16308
+ return /* @__PURE__ */ jsx(
16309
+ Injected,
16310
+ {
16311
+ product,
16312
+ cartId: resolved.cartId,
16313
+ createCart: resolved.createCart,
16314
+ onCartCreated: resolved.onCartCreated,
16315
+ includeTax: derived.useTax,
16316
+ labels: derived.labels,
16317
+ className: props.className
16318
+ }
16319
+ );
16204
16320
  }
16205
16321
  return /* @__PURE__ */ jsx(
16206
16322
  AddToCart_default,
@@ -16209,6 +16325,9 @@ function ProductInfoAddToCart(props = {}) {
16209
16325
  user: derived.user,
16210
16326
  currency: derived.currency,
16211
16327
  product,
16328
+ cartId: resolved.cartId,
16329
+ createCart: resolved.createCart,
16330
+ onCartCreated: resolved.onCartCreated,
16212
16331
  configuration: resolved.configuration,
16213
16332
  companyId: resolved.companyId,
16214
16333
  includeTax: derived.useTax,
@@ -16262,6 +16381,7 @@ function ProductInfoBulkPrices(props = {}) {
16262
16381
  bulkPrices: product.bulkPrices,
16263
16382
  includeTax: derived.useTax,
16264
16383
  portalMode: derived.portalMode,
16384
+ isAuthenticated: resolved.isAuthenticated,
16265
16385
  user: derived.user,
16266
16386
  taxZone: derived.taxZone,
16267
16387
  currency: derived.currency,
@@ -16424,7 +16544,7 @@ function ProductSlider(rawProps) {
16424
16544
  }
16425
16545
  function showAddToCart() {
16426
16546
  const allow = props.allowAddToCart !== false;
16427
- return !isContentHidden(props.portalMode, props.user) && allow;
16547
+ return !isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
16428
16548
  }
16429
16549
  function isCluster(item) {
16430
16550
  return "clusterId" in item && !("productId" in item);
@@ -16728,14 +16848,10 @@ function ProductVideos(props) {
16728
16848
  const hasItems = items.length > 0;
16729
16849
  const lang = props.language || "NL";
16730
16850
  function getVideoUri(video) {
16731
- const vids = video.videos || [];
16732
- const match = vids.find((v) => v.language === lang);
16733
- return match?.uri || vids?.[0]?.uri || "";
16851
+ return getLanguageUri(video.videos, lang, "");
16734
16852
  }
16735
16853
  function getVideoTitle(video) {
16736
- const alts = video.alt || [];
16737
- const match = alts.find((a) => a.language === lang);
16738
- return match?.value || alts?.[0]?.value || "Video";
16854
+ return getLanguageString(video.alt, lang, "Video");
16739
16855
  }
16740
16856
  function isEmbeddable(uri) {
16741
16857
  return uri.includes("youtube.com") || uri.includes("youtu.be") || uri.includes("vimeo.com");