@propeller-commerce/propeller-v2-react-ui 0.25.0 → 0.26.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.cjs CHANGED
@@ -815,7 +815,7 @@ function useCart(options) {
815
815
  rememberCart(resolved);
816
816
  return resolved;
817
817
  }, [graphqlClient, user, companyId, language, configuration, onCartCreated]);
818
- const addItem = React6.useCallback(async (opts) => {
818
+ const addItem2 = React6.useCallback(async (opts) => {
819
819
  setLoading(true);
820
820
  setError(null);
821
821
  try {
@@ -862,14 +862,14 @@ function useCart(options) {
862
862
  const added = [];
863
863
  let lastCart = null;
864
864
  for (const item of items) {
865
- const result = await addItem({ createCart: true, ...item });
865
+ const result = await addItem2({ createCart: true, ...item });
866
866
  if (!result.ok) return propellerV2CoreUi.err(result.error);
867
867
  lastCart = result.data.cart;
868
868
  added.push(result.data.item);
869
869
  }
870
870
  if (!lastCart) return propellerV2CoreUi.err("No items to add");
871
871
  return propellerV2CoreUi.ok({ cart: lastCart, items: added });
872
- }, [addItem]);
872
+ }, [addItem2]);
873
873
  const updateItemQuantity = React6.useCallback(async (cartItemId, quantity) => {
874
874
  if (!cartId) return void 0;
875
875
  setLoading(true);
@@ -990,7 +990,7 @@ function useCart(options) {
990
990
  return [];
991
991
  }
992
992
  }, [graphqlClient, language, user, resolvedCompanyId, imageSearchFilters, imageVariantFilters]);
993
- return { cart, cartId, loading, error, checkoutAllowed, resolveCart, addItem, addItems, updateItemQuantity, updateItemNotes, deleteItem, addActionCode, removeActionCode, requestAuthorization, processCart, getCrossupsells, getMinQuantity, getStep };
993
+ return { cart, cartId, loading, error, checkoutAllowed, resolveCart, addItem: addItem2, addItems, updateItemQuantity, updateItemNotes, deleteItem, addActionCode, removeActionCode, requestAuthorization, processCart, getCrossupsells, getMinQuantity, getStep };
994
994
  }
995
995
  function buildAddressInput(type, addr) {
996
996
  return {
@@ -2618,6 +2618,13 @@ function useProductSearch(options) {
2618
2618
  goToPage: pagination.goToPage
2619
2619
  };
2620
2620
  }
2621
+ function isPriceOnRequest(product) {
2622
+ if (!product) return false;
2623
+ const display = product.priceData?.display ?? product.defaultProduct?.priceData?.display;
2624
+ return display === propellerSdkV2.PriceDisplay.ON_REQUEST;
2625
+ }
2626
+
2627
+ // src/composables/react/useQuickOrder.ts
2621
2628
  function toMatch(item, language) {
2622
2629
  const isCluster = "clusterId" in item;
2623
2630
  const displayItem = isCluster ? item.defaultProduct : item;
@@ -2636,7 +2643,8 @@ function toMatch(item, language) {
2636
2643
  netPrice,
2637
2644
  grossPrice,
2638
2645
  minQuantity,
2639
- imageUrl
2646
+ imageUrl,
2647
+ priceOnRequest: isPriceOnRequest(item)
2640
2648
  };
2641
2649
  }
2642
2650
  function useQuickOrder(options) {
@@ -2783,6 +2791,146 @@ function useQuickOrder(options) {
2783
2791
  return { submitting, error, searchProducts, submit };
2784
2792
  }
2785
2793
 
2794
+ // src/composables/shared/utils/priceRequestList.ts
2795
+ function containsItem(list, code) {
2796
+ return list.some((i) => i.code === code);
2797
+ }
2798
+ function addItem(list, item) {
2799
+ if (containsItem(list, item.code)) return list;
2800
+ return [...list, { ...item, quantity: Math.max(item.quantity, item.minQuantity) }];
2801
+ }
2802
+ function removeItem(list, code) {
2803
+ return list.filter((i) => i.code !== code);
2804
+ }
2805
+ function setItemQuantity(list, code, quantity) {
2806
+ return list.map(
2807
+ (i) => i.code === code ? { ...i, quantity: Math.max(quantity, i.minQuantity) } : i
2808
+ );
2809
+ }
2810
+ function parseStoredList(raw) {
2811
+ if (!raw) return [];
2812
+ try {
2813
+ const parsed = JSON.parse(raw);
2814
+ if (!Array.isArray(parsed)) return [];
2815
+ return parsed.filter(
2816
+ (i) => i && typeof i.code === "string" && typeof i.productId === "number"
2817
+ );
2818
+ } catch {
2819
+ return [];
2820
+ }
2821
+ }
2822
+
2823
+ // src/composables/react/usePriceRequest.ts
2824
+ var PRICE_REQUEST_STORAGE_KEY = "propeller_price_request";
2825
+ var EMPTY = [];
2826
+ var cachedRaw = null;
2827
+ var cachedItems = EMPTY;
2828
+ function getSnapshot(key) {
2829
+ let raw = null;
2830
+ try {
2831
+ raw = window.localStorage.getItem(key);
2832
+ } catch {
2833
+ return EMPTY;
2834
+ }
2835
+ if (raw !== cachedRaw) {
2836
+ cachedRaw = raw;
2837
+ cachedItems = parseStoredList(raw);
2838
+ }
2839
+ return cachedItems;
2840
+ }
2841
+ function getServerSnapshot() {
2842
+ return EMPTY;
2843
+ }
2844
+ var listeners = /* @__PURE__ */ new Set();
2845
+ function subscribe(cb) {
2846
+ listeners.add(cb);
2847
+ window.addEventListener("storage", cb);
2848
+ return () => {
2849
+ listeners.delete(cb);
2850
+ window.removeEventListener("storage", cb);
2851
+ };
2852
+ }
2853
+ function write(key, next) {
2854
+ try {
2855
+ window.localStorage.setItem(key, JSON.stringify(next));
2856
+ } catch {
2857
+ }
2858
+ listeners.forEach((l) => l());
2859
+ }
2860
+ function usePriceRequest(options = {}) {
2861
+ const storageKey = options.storageKey ?? PRICE_REQUEST_STORAGE_KEY;
2862
+ const items = React6.useSyncExternalStore(
2863
+ subscribe,
2864
+ () => getSnapshot(storageKey),
2865
+ getServerSnapshot
2866
+ );
2867
+ const ready = React6.useSyncExternalStore(
2868
+ subscribe,
2869
+ () => true,
2870
+ () => false
2871
+ );
2872
+ const [submitting, setSubmitting] = React6.useState(false);
2873
+ const [error, setError] = React6.useState(null);
2874
+ const add = React6.useCallback(
2875
+ (item) => {
2876
+ const current = getSnapshot(storageKey);
2877
+ if (containsItem(current, item.code)) return false;
2878
+ write(storageKey, addItem(current, item));
2879
+ return true;
2880
+ },
2881
+ [storageKey]
2882
+ );
2883
+ const remove = React6.useCallback(
2884
+ (code) => write(storageKey, removeItem(getSnapshot(storageKey), code)),
2885
+ [storageKey]
2886
+ );
2887
+ const setQuantity = React6.useCallback(
2888
+ (code, quantity) => write(storageKey, setItemQuantity(getSnapshot(storageKey), code, quantity)),
2889
+ [storageKey]
2890
+ );
2891
+ const clear = React6.useCallback(() => write(storageKey, []), [storageKey]);
2892
+ const has = React6.useCallback((code) => containsItem(items, code), [items]);
2893
+ const submit = React6.useCallback(
2894
+ async (comment) => {
2895
+ const current = getSnapshot(storageKey);
2896
+ if (!current.length) {
2897
+ setError("empty");
2898
+ return false;
2899
+ }
2900
+ if (!options.onSubmit) {
2901
+ setError("unsupported");
2902
+ return false;
2903
+ }
2904
+ setSubmitting(true);
2905
+ setError(null);
2906
+ try {
2907
+ await options.onSubmit(current, comment);
2908
+ write(storageKey, []);
2909
+ return true;
2910
+ } catch (e) {
2911
+ setError(e?.message || "failed");
2912
+ return false;
2913
+ } finally {
2914
+ setSubmitting(false);
2915
+ }
2916
+ },
2917
+ [options, storageKey]
2918
+ );
2919
+ return {
2920
+ items,
2921
+ ready,
2922
+ count: items.length,
2923
+ add,
2924
+ remove,
2925
+ setQuantity,
2926
+ clear,
2927
+ has,
2928
+ submit,
2929
+ submitting,
2930
+ error
2931
+ };
2932
+ }
2933
+
2786
2934
  // src/composables/shared/utils/machineLanguage.ts
2787
2935
  var MACHINE_NOT_FOUND = "SPARE_PARTS_MACHINE_NOT_FOUND_ERROR";
2788
2936
  function isMachineNotFound(error) {
@@ -4623,7 +4771,7 @@ function mergeGrouped(p) {
4623
4771
  }
4624
4772
  function AddToCart(rawProps) {
4625
4773
  const props = mergeGrouped(useInfraProps(rawProps));
4626
- const { cart, loading, checkoutAllowed, addItem, getMinQuantity, getStep } = useCart({
4774
+ const { cart, loading, checkoutAllowed, addItem: addItem2, getMinQuantity, getStep } = useCart({
4627
4775
  graphqlClient: props.graphqlClient,
4628
4776
  user: props.user ?? null,
4629
4777
  companyId: props.companyId,
@@ -4730,7 +4878,7 @@ function AddToCart(rawProps) {
4730
4878
  if (!props.graphqlClient) return;
4731
4879
  if (props.beforeAddToCart && !props.beforeAddToCart()) return;
4732
4880
  const cartBeforeAdd = activeFullCart ?? cart ?? null;
4733
- const result = await addItem({
4881
+ const result = await addItem2({
4734
4882
  product: props.product,
4735
4883
  cluster: props.cluster,
4736
4884
  childItems: props.childItems,
@@ -6741,7 +6889,7 @@ function useCartItemContext() {
6741
6889
  }
6742
6890
  function CartItem(rawProps) {
6743
6891
  const props = useResolvedProps(rawProps, CART_ITEM_RESOLVE_SPEC);
6744
- const { updateItemQuantity, updateItemNotes, deleteItem, getCrossupsells, addItem, getMinQuantity, getStep } = useCart({
6892
+ const { updateItemQuantity, updateItemNotes, deleteItem, getCrossupsells, addItem: addItem2, getMinQuantity, getStep } = useCart({
6745
6893
  graphqlClient: props.graphqlClient,
6746
6894
  user: props.user ?? null,
6747
6895
  cartId: props.cartId,
@@ -6931,7 +7079,7 @@ function CartItem(rawProps) {
6931
7079
  const productId = getCrossupsellProductId(item);
6932
7080
  if (!productId) return;
6933
7081
  setAddingCrossupsellId(productId);
6934
- const result = await addItem({ product: { productId }, quantity: 1, cartId: props.cartId });
7082
+ const result = await addItem2({ product: { productId }, quantity: 1, cartId: props.cartId });
6935
7083
  setAddingCrossupsellId(null);
6936
7084
  if (result.ok && props.afterCartUpdate) {
6937
7085
  props.afterCartUpdate(result.data.cart);
@@ -8300,6 +8448,7 @@ function ClusterCard(rawProps) {
8300
8448
  const contentHidden = propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8301
8449
  const defaultProductInventory = contentHidden ? void 0 : cluster?.defaultProduct?.inventory;
8302
8450
  const useTax = props.includeTax !== void 0 ? !!props.includeTax : false;
8451
+ const priceOnRequest = isPriceOnRequest(cluster);
8303
8452
  const priceObj = contentHidden ? void 0 : cluster?.defaultProduct?.price;
8304
8453
  const priceValue = useTax ? priceObj?.net : priceObj?.gross;
8305
8454
  const clusterPrice = props.showPrice === false ? "" : propellerV2CoreUi.formatPrice(priceValue, { symbol: props.currency ?? "\u20AC", locale: propellerV2CoreUi.localeForLanguage(props.language) });
@@ -8334,6 +8483,7 @@ function ClusterCard(rawProps) {
8334
8483
  defaultProductInventory,
8335
8484
  clusterPrice,
8336
8485
  priceObj,
8486
+ priceOnRequest,
8337
8487
  useTax,
8338
8488
  isRow,
8339
8489
  language,
@@ -8372,7 +8522,7 @@ function ClusterCard(rawProps) {
8372
8522
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cluster-card__footer w-full md:w-auto flex flex-col gap-2 md:flex-row md:items-center md:gap-3 px-4 py-2 md:py-0 border-t md:border-t-0 border-border-subtle", children: [
8373
8523
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cluster-card__footer-meta flex items-center justify-between gap-3 md:contents", children: [
8374
8524
  props.showStock && defaultProductInventory ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardStock, {}) : null,
8375
- props.showPrice !== false && priceObj ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8525
+ props.showPrice !== false && (priceObj || priceOnRequest) ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8376
8526
  ] }),
8377
8527
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cluster-card__cta w-full md:w-auto md:flex-shrink-0 md:ml-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ClusterCardViewClusterLink, {}) })
8378
8528
  ] })
@@ -8384,11 +8534,11 @@ function ClusterCard(rawProps) {
8384
8534
  textLabelValues.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardTextLabels, {}) : null,
8385
8535
  props.showManufacturer && manufacturer ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardManufacturer, {}) : null,
8386
8536
  props.showShortDescription && shortDescription ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardShortDescription, {}) : null,
8387
- props.showPrice !== false && priceObj ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto hidden md:block", children: /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) }) : null
8537
+ props.showPrice !== false && (priceObj || priceOnRequest) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto hidden md:block", children: /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) }) : null
8388
8538
  ] }),
8389
- props.showStock && defaultProductInventory || props.showPrice !== false && priceObj ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cluster-card__footer-meta flex flex-wrap items-center justify-between gap-x-2 gap-y-1 px-3 pt-1 sm:px-4 md:hidden", children: [
8539
+ props.showStock && defaultProductInventory || props.showPrice !== false && (priceObj || priceOnRequest) ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cluster-card__footer-meta flex flex-wrap items-center justify-between gap-x-2 gap-y-1 px-3 pt-1 sm:px-4 md:hidden", children: [
8390
8540
  props.showStock && defaultProductInventory ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardStock, {}) : null,
8391
- props.showPrice !== false && priceObj ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8541
+ props.showPrice !== false && (priceObj || priceOnRequest) ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8392
8542
  ] }) : null,
8393
8543
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-cluster-card__cta px-3 pb-3 pt-2 sm:px-4 sm:pb-4", children: /* @__PURE__ */ jsxRuntime.jsx(ClusterCardViewClusterLink, {}) })
8394
8544
  ] })
@@ -8594,6 +8744,9 @@ function ClusterCardStock(props) {
8594
8744
  }
8595
8745
  function ClusterCardPrice(props) {
8596
8746
  const { derived, resolved } = useClusterCard();
8747
+ if (derived.priceOnRequest) {
8748
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: props.className ?? "propeller-cluster-card__price", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-cluster-card__price-on-request text-sm text-foreground", children: propellerV2CoreUi.getLabel(resolved.labels, "priceOnRequest", "Price on request") }) });
8749
+ }
8597
8750
  if (!derived.priceObj) return null;
8598
8751
  const InjectedPrice = resolved.priceComponent;
8599
8752
  if (InjectedPrice) {
@@ -8773,7 +8926,8 @@ function ProductPriceDisplay(props) {
8773
8926
  }
8774
8927
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(`propeller-product-price ${props.className || ""}`), "data-hidden": isHidden2() ? "true" : "false", children: [
8775
8928
  isHidden2() && props.showLoginPrompt !== false ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-product-price__login-prompt text-sm text-muted-foreground italic", children: propellerV2CoreUi.getLabel(props.labels, "loginToSeePrices", "Log in to see prices") }) : null,
8776
- !isHidden2() && !!getLeadingPrice() ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-product-price__content flex flex-col gap-0.5", children: [
8929
+ !isHidden2() && props.priceOnRequest ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-product-price__on-request text-sm text-foreground", children: propellerV2CoreUi.getLabel(props.labels, "priceOnRequest", "Price on request") }) : null,
8930
+ !isHidden2() && !props.priceOnRequest && !!getLeadingPrice() ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-product-price__content flex flex-col gap-0.5", children: [
8777
8931
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-product-price__primary flex items-baseline gap-2", children: [
8778
8932
  /* @__PURE__ */ jsxRuntime.jsx(
8779
8933
  "span",
@@ -9149,6 +9303,20 @@ function ProductCardBelowName() {
9149
9303
  }
9150
9304
  function ProductCardPrice(props) {
9151
9305
  const { product, derived, resolved } = useProductCard();
9306
+ const onRequest = isPriceOnRequest(product);
9307
+ if (onRequest) {
9308
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: props.className ?? "propeller-product-card__price", children: /* @__PURE__ */ jsxRuntime.jsx(
9309
+ ProductPrice_default,
9310
+ {
9311
+ priceOnRequest: true,
9312
+ labels: resolved.priceLabels,
9313
+ portalMode: resolved.portalMode,
9314
+ isAuthenticated: resolved.isAuthenticated,
9315
+ user: resolved.user,
9316
+ showLoginPrompt: false
9317
+ }
9318
+ ) });
9319
+ }
9152
9320
  if (!product?.price) return null;
9153
9321
  const InjectedPrice = resolved.priceComponent;
9154
9322
  if (InjectedPrice) {
@@ -10129,7 +10297,7 @@ function GridToolbar(rawProps) {
10129
10297
  const [currentSortOrder, setCurrentSortOrder] = React6.useState(propellerSdkV2.SortOrder.DESC);
10130
10298
  const [currentOffset, setCurrentOffset] = React6.useState(12);
10131
10299
  const [currentViewMode, setCurrentViewMode] = React6.useState("grid");
10132
- function getLabel59(key) {
10300
+ function getLabel61(key) {
10133
10301
  const labels = props.labels || {};
10134
10302
  return labels[key] !== void 0 ? labels[key] : DEFAULT_LABELS[key] || key;
10135
10303
  }
@@ -10167,8 +10335,8 @@ function GridToolbar(rawProps) {
10167
10335
  }
10168
10336
  function getAvailabilityLabel() {
10169
10337
  const qty = props.minStock;
10170
- if (qty !== void 0 && qty > propellerV2CoreUi.MIN_STOCK_THRESHOLD) return `${getLabel59("inStock")}: ${qty}+`;
10171
- return getLabel59("inStock");
10338
+ if (qty !== void 0 && qty > propellerV2CoreUi.MIN_STOCK_THRESHOLD) return `${getLabel61("inStock")}: ${qty}+`;
10339
+ return getLabel61("inStock");
10172
10340
  }
10173
10341
  function handleSortFieldChange(field) {
10174
10342
  setCurrentSortField(field);
@@ -10214,7 +10382,7 @@ function GridToolbar(rawProps) {
10214
10382
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-grid-toolbar__count text-sm text-muted-foreground font-medium", children: props.itemsFound > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
10215
10383
  props.itemsFound,
10216
10384
  "\xA0",
10217
- props.itemsFound === 1 ? getLabel59("productSingular") : getLabel59("productPlural")
10385
+ props.itemsFound === 1 ? getLabel61("productSingular") : getLabel61("productPlural")
10218
10386
  ] }) : null }),
10219
10387
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-grid-toolbar__controls flex flex-wrap items-center gap-3", children: [
10220
10388
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -10225,7 +10393,7 @@ function GridToolbar(rawProps) {
10225
10393
  onChange: (e) => handleOffsetChange(parseInt(e.target.value)),
10226
10394
  children: getOffsetOptions()?.map((n) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: n, children: [
10227
10395
  n,
10228
- getLabel59("perPage")
10396
+ getLabel61("perPage")
10229
10397
  ] }, n))
10230
10398
  }
10231
10399
  ),
@@ -10241,7 +10409,7 @@ function GridToolbar(rawProps) {
10241
10409
  {
10242
10410
  value: field,
10243
10411
  disabled: field === "PRICE" && isPriceSortDisabled(),
10244
- children: getLabel59(field)
10412
+ children: getLabel61(field)
10245
10413
  },
10246
10414
  field
10247
10415
  ))
@@ -10254,8 +10422,8 @@ function GridToolbar(rawProps) {
10254
10422
  value: currentSortOrder,
10255
10423
  onChange: (e) => handleSortOrderChange(e.target.value),
10256
10424
  children: [
10257
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: propellerSdkV2.SortOrder.ASC, children: getLabel59("ASC") }),
10258
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: propellerSdkV2.SortOrder.DESC, children: getLabel59("DESC") })
10425
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: propellerSdkV2.SortOrder.ASC, children: getLabel61("ASC") }),
10426
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: propellerSdkV2.SortOrder.DESC, children: getLabel61("DESC") })
10259
10427
  ]
10260
10428
  }
10261
10429
  ),
@@ -10265,7 +10433,7 @@ function GridToolbar(rawProps) {
10265
10433
  type: "button",
10266
10434
  className: "propeller-grid-toolbar__view-toggle h-9 w-9 flex items-center justify-center rounded-control border border-input bg-transparent hover:bg-accent hover:text-accent-foreground transition-colors",
10267
10435
  onClick: (event) => handleViewChange(),
10268
- title: currentViewMode === "grid" ? getLabel59("switchToList") : getLabel59("switchToGrid"),
10436
+ title: currentViewMode === "grid" ? getLabel61("switchToList") : getLabel61("switchToGrid"),
10269
10437
  children: [
10270
10438
  currentViewMode === "grid" ? /* @__PURE__ */ jsxRuntime.jsxs(
10271
10439
  "svg",
@@ -10323,7 +10491,7 @@ function GridToolbar(rawProps) {
10323
10491
  onClick: (event) => {
10324
10492
  if (props.onClearFilters) props.onClearFilters();
10325
10493
  },
10326
- children: getLabel59("clearAll")
10494
+ children: getLabel61("clearAll")
10327
10495
  }
10328
10496
  ),
10329
10497
  props.priceFilterMin !== void 0 || props.priceFilterMax !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
@@ -10334,7 +10502,7 @@ function GridToolbar(rawProps) {
10334
10502
  if (props.onPriceFilterRemove) props.onPriceFilterRemove();
10335
10503
  },
10336
10504
  children: [
10337
- getLabel59("price"),
10505
+ getLabel61("price"),
10338
10506
  ": ",
10339
10507
  currencySymbol,
10340
10508
  props.priceFilterMin ?? 0,
@@ -10386,7 +10554,7 @@ var DEFAULT_LABELS2 = {
10386
10554
  of: "of"
10387
10555
  };
10388
10556
  function GridPagination(props) {
10389
- function getLabel59(key) {
10557
+ function getLabel61(key) {
10390
10558
  const labels = props.labels || {};
10391
10559
  return labels[key] !== void 0 ? labels[key] : DEFAULT_LABELS2[key] || key;
10392
10560
  }
@@ -10459,15 +10627,15 @@ function GridPagination(props) {
10459
10627
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--prev inline-flex items-center rounded-control border border-input bg-card px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10460
10628
  disabled: currentPage === 1,
10461
10629
  onClick: () => handlePageChange(currentPage - 1),
10462
- children: getLabel59("previous")
10630
+ children: getLabel61("previous")
10463
10631
  }
10464
10632
  ),
10465
10633
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "propeller-grid-pagination__info px-2 text-sm font-medium text-muted-foreground", children: [
10466
- getLabel59("page"),
10634
+ getLabel61("page"),
10467
10635
  "\xA0",
10468
10636
  currentPage,
10469
10637
  "\xA0",
10470
- getLabel59("of"),
10638
+ getLabel61("of"),
10471
10639
  "\xA0",
10472
10640
  totalPages
10473
10641
  ] }),
@@ -10478,7 +10646,7 @@ function GridPagination(props) {
10478
10646
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--next inline-flex items-center rounded-control border border-input bg-card px-4 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10479
10647
  disabled: currentPage === totalPages,
10480
10648
  onClick: () => handlePageChange(currentPage + 1),
10481
- children: getLabel59("next")
10649
+ children: getLabel61("next")
10482
10650
  }
10483
10651
  )
10484
10652
  ] }) : null,
@@ -10490,7 +10658,7 @@ function GridPagination(props) {
10490
10658
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--prev inline-flex items-center rounded-control border border-input bg-card px-3 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10491
10659
  disabled: currentPage === 1,
10492
10660
  onClick: () => handlePageChange(currentPage - 1),
10493
- children: getLabel59("previous")
10661
+ children: getLabel61("previous")
10494
10662
  }
10495
10663
  ),
10496
10664
  getFullPages().map((item, idx) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -10517,7 +10685,7 @@ function GridPagination(props) {
10517
10685
  className: "propeller-grid-pagination__btn propeller-grid-pagination__btn--next inline-flex items-center rounded-control border border-input bg-card px-3 py-2 text-sm font-medium text-foreground shadow-sm hover:bg-surface-hover disabled:opacity-40 disabled:cursor-not-allowed",
10518
10686
  disabled: currentPage === totalPages,
10519
10687
  onClick: () => handlePageChange(currentPage + 1),
10520
- children: getLabel59("next")
10688
+ children: getLabel61("next")
10521
10689
  }
10522
10690
  )
10523
10691
  ] }) : null
@@ -11820,7 +11988,7 @@ function FavoriteListDetails(rawProps) {
11820
11988
  graphqlClient: props.graphqlClient,
11821
11989
  user: props.user ?? null
11822
11990
  });
11823
- const { addItem } = useCart({
11991
+ const { addItem: addItem2 } = useCart({
11824
11992
  graphqlClient: props.graphqlClient,
11825
11993
  user: props.user ?? null,
11826
11994
  cartId: props.cartId,
@@ -11925,7 +12093,7 @@ function FavoriteListDetails(rawProps) {
11925
12093
  setBulkBusy(true);
11926
12094
  try {
11927
12095
  for (const product of products) {
11928
- await addItem({
12096
+ await addItem2({
11929
12097
  product,
11930
12098
  quantity: product.minimumQuantity && product.minimumQuantity > 0 ? product.minimumQuantity : 1,
11931
12099
  cartId: props.cartId,
@@ -14391,6 +14559,130 @@ function OrderList(rawProps) {
14391
14559
  ] });
14392
14560
  }
14393
14561
  var OrderList_default = OrderList;
14562
+ function PriceRequestList(props) {
14563
+ const [comment, setComment] = React6.useState("");
14564
+ const [sent, setSent] = React6.useState(false);
14565
+ const L = (key, fallback) => propellerV2CoreUi.getLabel(props.labels, key, fallback);
14566
+ async function handleSubmit(e) {
14567
+ e.preventDefault();
14568
+ if (!props.onSubmit) return;
14569
+ const ok4 = await props.onSubmit(comment);
14570
+ if (ok4) {
14571
+ setComment("");
14572
+ setSent(true);
14573
+ }
14574
+ }
14575
+ if (sent) {
14576
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(`propeller-price-request ${props.className || ""}`), children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-price-request__sent py-8 text-center text-foreground", children: L("sent", "Price request sent. We will contact you.") }) });
14577
+ }
14578
+ if (!props.items.length) {
14579
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(`propeller-price-request ${props.className || ""}`), children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-price-request__empty py-8 text-center text-foreground-subtle", children: L("empty", "Your price request list is empty.") }) });
14580
+ }
14581
+ return /* @__PURE__ */ jsxRuntime.jsxs(
14582
+ "form",
14583
+ {
14584
+ className: cn(`propeller-price-request ${props.className || ""}`),
14585
+ onSubmit: handleSubmit,
14586
+ children: [
14587
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "propeller-price-request__title mb-4 text-xl font-semibold text-foreground", children: L("title", "Price request") }),
14588
+ /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "propeller-price-request__table w-full text-sm", children: [
14589
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b border-border text-left text-foreground-subtle", children: [
14590
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "py-2 pr-4 font-medium", children: L("colCode", "Article no. / SKU") }),
14591
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "py-2 pr-4 font-medium", children: L("colName", "Product name") }),
14592
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "py-2 pr-4 font-medium", children: L("colQuantity", "Quantity") }),
14593
+ /* @__PURE__ */ jsxRuntime.jsx("th", { scope: "col", className: "py-2 w-10", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: L("remove", "Remove") }) })
14594
+ ] }) }),
14595
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: props.items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "border-b border-border-subtle", children: [
14596
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 pr-4 font-mono text-xs text-foreground", children: item.code }),
14597
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 pr-4 text-foreground", children: item.name }),
14598
+ /* @__PURE__ */ jsxRuntime.jsxs("td", { className: "py-2 pr-4", children: [
14599
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "sr-only", htmlFor: `pr-qty-${item.code}`, children: L("colQuantity", "Quantity") }),
14600
+ /* @__PURE__ */ jsxRuntime.jsx(
14601
+ "input",
14602
+ {
14603
+ id: `pr-qty-${item.code}`,
14604
+ type: "number",
14605
+ min: item.minQuantity,
14606
+ step: item.unit || 1,
14607
+ value: item.quantity,
14608
+ onChange: (e) => props.onQuantityChange?.(item.code, parseInt(e.target.value, 10) || item.minQuantity),
14609
+ className: "w-20 rounded border border-border bg-background px-2 py-1"
14610
+ }
14611
+ )
14612
+ ] }),
14613
+ /* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2", children: /* @__PURE__ */ jsxRuntime.jsx(
14614
+ "button",
14615
+ {
14616
+ type: "button",
14617
+ onClick: () => props.onRemove?.(item.code),
14618
+ "aria-label": L("remove", "Remove"),
14619
+ title: L("remove", "Remove"),
14620
+ className: "text-foreground-subtle transition-colors hover:text-destructive",
14621
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4", "aria-hidden": "true" })
14622
+ }
14623
+ ) })
14624
+ ] }, item.code)) })
14625
+ ] }),
14626
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-price-request__comment mt-6", children: [
14627
+ /* @__PURE__ */ jsxRuntime.jsx(
14628
+ "label",
14629
+ {
14630
+ htmlFor: "propeller-price-request-comment",
14631
+ className: "mb-2 block font-medium text-foreground",
14632
+ children: L("comments", "Comments")
14633
+ }
14634
+ ),
14635
+ /* @__PURE__ */ jsxRuntime.jsx(
14636
+ "textarea",
14637
+ {
14638
+ id: "propeller-price-request-comment",
14639
+ value: comment,
14640
+ onChange: (e) => setComment(e.target.value),
14641
+ rows: 4,
14642
+ className: "w-full rounded border border-border bg-background px-3 py-2"
14643
+ }
14644
+ )
14645
+ ] }),
14646
+ /* @__PURE__ */ jsxRuntime.jsx(
14647
+ "button",
14648
+ {
14649
+ type: "submit",
14650
+ disabled: props.submitting,
14651
+ className: "propeller-price-request__submit mt-4 rounded-control bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity hover:opacity-90 disabled:opacity-50",
14652
+ children: props.submitting ? L("sending", "Sending\u2026") : L("send", "Send request")
14653
+ }
14654
+ )
14655
+ ]
14656
+ }
14657
+ );
14658
+ }
14659
+ var PriceRequestList_default = PriceRequestList;
14660
+ function RequestPriceButton(props) {
14661
+ const anonymous = !props.isAuthenticated;
14662
+ const label = anonymous ? propellerV2CoreUi.getLabel(props.labels, "requestPrice", "Request a price") : props.added ? propellerV2CoreUi.getLabel(props.labels, "priceRequested", "On your request list") : propellerV2CoreUi.getLabel(props.labels, "requestPrice", "Request a price");
14663
+ return /* @__PURE__ */ jsxRuntime.jsxs(
14664
+ "button",
14665
+ {
14666
+ type: "button",
14667
+ "data-added": props.added ? "true" : "false",
14668
+ className: cn(
14669
+ `propeller-request-price inline-flex items-center justify-center gap-2 h-10 px-4 w-full rounded-control text-sm font-medium transition-opacity hover:opacity-90 ${props.added && !anonymous ? "bg-surface-hover text-foreground border border-border" : "bg-primary text-primary-foreground"} ${props.className || ""}`
14670
+ ),
14671
+ onClick: () => {
14672
+ if (anonymous) {
14673
+ if (props.onLoginClick) props.onLoginClick();
14674
+ return;
14675
+ }
14676
+ if (props.onRequestPrice) props.onRequestPrice();
14677
+ },
14678
+ children: [
14679
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareQuote, { className: "propeller-request-price__icon w-4 h-4", "aria-hidden": "true" }),
14680
+ label
14681
+ ]
14682
+ }
14683
+ );
14684
+ }
14685
+ var RequestPriceButton_default = RequestPriceButton;
14394
14686
  var ROW_SEQ = 0;
14395
14687
  function blankRow() {
14396
14688
  ROW_SEQ += 1;
@@ -14482,6 +14774,11 @@ function QuickOrderInner(props) {
14482
14774
  patchRow(key, { code: "", matches: [], productId: null, name: "", netPrice: 0, grossPrice: 0, searched: false });
14483
14775
  return;
14484
14776
  }
14777
+ if (match.priceOnRequest) {
14778
+ setNotice(L("priceOnRequest", "Price on request \u2014 order this product from its page"));
14779
+ patchRow(key, { code: "", matches: [], productId: null, name: "", netPrice: 0, grossPrice: 0, searched: false });
14780
+ return;
14781
+ }
14485
14782
  patchRow(key, {
14486
14783
  code: match.sku,
14487
14784
  productId: match.productId,
@@ -14542,6 +14839,10 @@ function QuickOrderInner(props) {
14542
14839
  continue;
14543
14840
  }
14544
14841
  if (resolved.some((r) => r.productId === exact.productId)) continue;
14842
+ if (exact.priceOnRequest) {
14843
+ notFound.push(line.code);
14844
+ continue;
14845
+ }
14545
14846
  const row = blankRow();
14546
14847
  resolved.push({
14547
14848
  ...row,
@@ -16331,6 +16632,20 @@ function ProductInfoAddToCart(props = {}) {
16331
16632
  }
16332
16633
  );
16333
16634
  }
16635
+ if (product?.orderable === propellerSdkV2.YesNo.N) return null;
16636
+ if (isPriceOnRequest(product)) {
16637
+ return /* @__PURE__ */ jsxRuntime.jsx(
16638
+ RequestPriceButton_default,
16639
+ {
16640
+ className: props.className,
16641
+ labels: derived.labels,
16642
+ isAuthenticated: resolved.isAuthenticated,
16643
+ onLoginClick: resolved.onLoginClick,
16644
+ added: resolved.priceRequestAdded,
16645
+ onRequestPrice: resolved.onRequestPrice ? () => resolved.onRequestPrice(product) : void 0
16646
+ }
16647
+ );
16648
+ }
16334
16649
  const Injected = resolved.addToCartComponent;
16335
16650
  if (Injected) {
16336
16651
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -18895,7 +19210,8 @@ function mapToSearchBarResult(item, language, urls) {
18895
19210
  priceGross,
18896
19211
  imageUrl: displayItem?.media?.images?.items?.[0]?.imageVariants?.[0]?.url || "",
18897
19212
  url,
18898
- isCluster
19213
+ isCluster,
19214
+ priceOnRequest: isPriceOnRequest(item)
18899
19215
  };
18900
19216
  }
18901
19217
  function SearchBar(rawProps) {
@@ -19079,7 +19395,7 @@ function SearchBar(rawProps) {
19079
19395
  result.sku
19080
19396
  ] }) : null
19081
19397
  ] }),
19082
- props.renderPrice ? props.renderPrice(result) : props.showPrice !== false && result.price !== void 0 && result.price !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-search-bar__result-price text-sm font-semibold text-foreground flex-shrink-0 text-right", children: [
19398
+ props.renderPrice ? props.renderPrice(result) : props.showPrice !== false && result.priceOnRequest ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-search-bar__result-price text-sm text-foreground flex-shrink-0 text-right", children: propellerV2CoreUi.getLabel(props.priceLabels, "priceOnRequest", "Price on request") }) : props.showPrice !== false && result.price !== void 0 && result.price !== null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-search-bar__result-price text-sm font-semibold text-foreground flex-shrink-0 text-right", children: [
19083
19399
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-search-bar__result-price-value", children: formatItemPrice(leadingPrice(result)) }),
19084
19400
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-search-bar__result-price-label block text-xs font-normal text-muted-foreground", children: priceTaxLabel() })
19085
19401
  ] }) : null
@@ -19287,6 +19603,8 @@ exports.OrderList = OrderList_default;
19287
19603
  exports.OrderShipments = OrderShipments_default;
19288
19604
  exports.OrderSummary = OrderSummaryWithProvider_default;
19289
19605
  exports.OrderTotals = OrderTotalsWithProvider_default;
19606
+ exports.PRICE_REQUEST_STORAGE_KEY = PRICE_REQUEST_STORAGE_KEY;
19607
+ exports.PriceRequestList = PriceRequestList_default;
19290
19608
  exports.PriceToggle = PriceToggle_default;
19291
19609
  exports.ProductBulkPrices = ProductBulkPricesWithProvider_default;
19292
19610
  exports.ProductBundles = ProductBundles_default;
@@ -19310,12 +19628,18 @@ exports.PurchaseAuthorizationRequests = PurchaseAuthorizationRequests_default;
19310
19628
  exports.QuickOrder = QuickOrder;
19311
19629
  exports.QuoteActions = QuoteActions_default;
19312
19630
  exports.RegisterForm = RegisterForm_default;
19631
+ exports.RequestPriceButton = RequestPriceButton_default;
19313
19632
  exports.SearchBar = SearchBar_default;
19314
19633
  exports.UserDetails = UserDetails_default;
19634
+ exports.addPriceRequestItem = addItem;
19315
19635
  exports.buildRootMachinesQuery = buildRootMachinesQuery;
19316
19636
  exports.fetchActiveCart = fetchActiveCart;
19317
19637
  exports.initCart = initCart;
19638
+ exports.isPriceOnRequest = isPriceOnRequest;
19318
19639
  exports.mergeAnonymousCart = mergeAnonymousCart;
19640
+ exports.priceRequestContains = containsItem;
19641
+ exports.removePriceRequestItem = removeItem;
19642
+ exports.setPriceRequestItemQuantity = setItemQuantity;
19319
19643
  exports.useAddress = useAddress;
19320
19644
  exports.useAuth = useAuth;
19321
19645
  exports.useCart = useCart;
@@ -19327,6 +19651,7 @@ exports.useInfraProps = useInfraProps;
19327
19651
  exports.useMachines = useMachines;
19328
19652
  exports.useMenu = useMenu;
19329
19653
  exports.useOrders = useOrders;
19654
+ exports.usePriceRequest = usePriceRequest;
19330
19655
  exports.useProductBundles = useProductBundles;
19331
19656
  exports.useProductGridConfig = useProductGridConfig;
19332
19657
  exports.useProductInfo = useProductInfo;