@propeller-commerce/propeller-v2-react-ui 0.24.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);
@@ -7354,6 +7502,8 @@ function CartItemDelete(props = {}) {
7354
7502
  className: props.className ?? "propeller-cart-item__delete h-8 w-8 p-0 ml-auto inline-flex items-center justify-center rounded-control text-foreground-subtle hover:text-destructive hover:bg-destructive/10 transition-colors disabled:opacity-50",
7355
7503
  onClick: () => handlers.onDelete(),
7356
7504
  disabled: state.deleting,
7505
+ "aria-label": propellerV2CoreUi.getLabel(resolved.labels, "deleteLabel", "Remove item"),
7506
+ title: propellerV2CoreUi.getLabel(resolved.labels, "deleteLabel", "Remove item"),
7357
7507
  children: [
7358
7508
  state.deleting ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-4 h-4 border-2 border-foreground-subtle border-t-transparent rounded-full animate-spin" }) : null,
7359
7509
  !state.deleting ? /* @__PURE__ */ jsxRuntime.jsxs(
@@ -7734,7 +7884,8 @@ function CartOverview(rawProps) {
7734
7884
  const invoiceAddress = props.cart?.invoiceAddress;
7735
7885
  const deliveryAddress = props.cart?.deliveryAddress;
7736
7886
  const getCountryName = (code) => propellerV2CoreUi.getCountryName(code, props.countries);
7737
- const paymentMethod = props.cart?.paymentData?.method || "";
7887
+ const rawPaymentMethod = props.cart?.paymentData?.method || "";
7888
+ const paymentMethod = rawPaymentMethod ? props.paymethodLabels?.[rawPaymentMethod.toLowerCase()] || rawPaymentMethod : "";
7738
7889
  const carrierName = props.cart?.postageData?.carrier || "";
7739
7890
  function requestDate() {
7740
7891
  const date = props.cart?.postageData?.requestDate;
@@ -8297,6 +8448,7 @@ function ClusterCard(rawProps) {
8297
8448
  const contentHidden = propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
8298
8449
  const defaultProductInventory = contentHidden ? void 0 : cluster?.defaultProduct?.inventory;
8299
8450
  const useTax = props.includeTax !== void 0 ? !!props.includeTax : false;
8451
+ const priceOnRequest = isPriceOnRequest(cluster);
8300
8452
  const priceObj = contentHidden ? void 0 : cluster?.defaultProduct?.price;
8301
8453
  const priceValue = useTax ? priceObj?.net : priceObj?.gross;
8302
8454
  const clusterPrice = props.showPrice === false ? "" : propellerV2CoreUi.formatPrice(priceValue, { symbol: props.currency ?? "\u20AC", locale: propellerV2CoreUi.localeForLanguage(props.language) });
@@ -8331,6 +8483,7 @@ function ClusterCard(rawProps) {
8331
8483
  defaultProductInventory,
8332
8484
  clusterPrice,
8333
8485
  priceObj,
8486
+ priceOnRequest,
8334
8487
  useTax,
8335
8488
  isRow,
8336
8489
  language,
@@ -8369,7 +8522,7 @@ function ClusterCard(rawProps) {
8369
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: [
8370
8523
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-cluster-card__footer-meta flex items-center justify-between gap-3 md:contents", children: [
8371
8524
  props.showStock && defaultProductInventory ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardStock, {}) : null,
8372
- props.showPrice !== false && priceObj ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8525
+ props.showPrice !== false && (priceObj || priceOnRequest) ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8373
8526
  ] }),
8374
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, {}) })
8375
8528
  ] })
@@ -8381,11 +8534,11 @@ function ClusterCard(rawProps) {
8381
8534
  textLabelValues.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardTextLabels, {}) : null,
8382
8535
  props.showManufacturer && manufacturer ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardManufacturer, {}) : null,
8383
8536
  props.showShortDescription && shortDescription ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardShortDescription, {}) : null,
8384
- 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
8385
8538
  ] }),
8386
- 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: [
8387
8540
  props.showStock && defaultProductInventory ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardStock, {}) : null,
8388
- props.showPrice !== false && priceObj ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8541
+ props.showPrice !== false && (priceObj || priceOnRequest) ? /* @__PURE__ */ jsxRuntime.jsx(ClusterCardPrice, {}) : null
8389
8542
  ] }) : null,
8390
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, {}) })
8391
8544
  ] })
@@ -8591,6 +8744,9 @@ function ClusterCardStock(props) {
8591
8744
  }
8592
8745
  function ClusterCardPrice(props) {
8593
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
+ }
8594
8750
  if (!derived.priceObj) return null;
8595
8751
  const InjectedPrice = resolved.priceComponent;
8596
8752
  if (InjectedPrice) {
@@ -8770,7 +8926,8 @@ function ProductPriceDisplay(props) {
8770
8926
  }
8771
8927
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(`propeller-product-price ${props.className || ""}`), "data-hidden": isHidden2() ? "true" : "false", children: [
8772
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,
8773
- !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: [
8774
8931
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-product-price__primary flex items-baseline gap-2", children: [
8775
8932
  /* @__PURE__ */ jsxRuntime.jsx(
8776
8933
  "span",
@@ -9146,6 +9303,20 @@ function ProductCardBelowName() {
9146
9303
  }
9147
9304
  function ProductCardPrice(props) {
9148
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
+ }
9149
9320
  if (!product?.price) return null;
9150
9321
  const InjectedPrice = resolved.priceComponent;
9151
9322
  if (InjectedPrice) {
@@ -10126,7 +10297,7 @@ function GridToolbar(rawProps) {
10126
10297
  const [currentSortOrder, setCurrentSortOrder] = React6.useState(propellerSdkV2.SortOrder.DESC);
10127
10298
  const [currentOffset, setCurrentOffset] = React6.useState(12);
10128
10299
  const [currentViewMode, setCurrentViewMode] = React6.useState("grid");
10129
- function getLabel59(key) {
10300
+ function getLabel61(key) {
10130
10301
  const labels = props.labels || {};
10131
10302
  return labels[key] !== void 0 ? labels[key] : DEFAULT_LABELS[key] || key;
10132
10303
  }
@@ -10164,8 +10335,8 @@ function GridToolbar(rawProps) {
10164
10335
  }
10165
10336
  function getAvailabilityLabel() {
10166
10337
  const qty = props.minStock;
10167
- if (qty !== void 0 && qty > propellerV2CoreUi.MIN_STOCK_THRESHOLD) return `${getLabel59("inStock")}: ${qty}+`;
10168
- return getLabel59("inStock");
10338
+ if (qty !== void 0 && qty > propellerV2CoreUi.MIN_STOCK_THRESHOLD) return `${getLabel61("inStock")}: ${qty}+`;
10339
+ return getLabel61("inStock");
10169
10340
  }
10170
10341
  function handleSortFieldChange(field) {
10171
10342
  setCurrentSortField(field);
@@ -10211,7 +10382,7 @@ function GridToolbar(rawProps) {
10211
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: [
10212
10383
  props.itemsFound,
10213
10384
  "\xA0",
10214
- props.itemsFound === 1 ? getLabel59("productSingular") : getLabel59("productPlural")
10385
+ props.itemsFound === 1 ? getLabel61("productSingular") : getLabel61("productPlural")
10215
10386
  ] }) : null }),
10216
10387
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "propeller-grid-toolbar__controls flex flex-wrap items-center gap-3", children: [
10217
10388
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -10222,7 +10393,7 @@ function GridToolbar(rawProps) {
10222
10393
  onChange: (e) => handleOffsetChange(parseInt(e.target.value)),
10223
10394
  children: getOffsetOptions()?.map((n) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: n, children: [
10224
10395
  n,
10225
- getLabel59("perPage")
10396
+ getLabel61("perPage")
10226
10397
  ] }, n))
10227
10398
  }
10228
10399
  ),
@@ -10238,7 +10409,7 @@ function GridToolbar(rawProps) {
10238
10409
  {
10239
10410
  value: field,
10240
10411
  disabled: field === "PRICE" && isPriceSortDisabled(),
10241
- children: getLabel59(field)
10412
+ children: getLabel61(field)
10242
10413
  },
10243
10414
  field
10244
10415
  ))
@@ -10251,8 +10422,8 @@ function GridToolbar(rawProps) {
10251
10422
  value: currentSortOrder,
10252
10423
  onChange: (e) => handleSortOrderChange(e.target.value),
10253
10424
  children: [
10254
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: propellerSdkV2.SortOrder.ASC, children: getLabel59("ASC") }),
10255
- /* @__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") })
10256
10427
  ]
10257
10428
  }
10258
10429
  ),
@@ -10262,7 +10433,7 @@ function GridToolbar(rawProps) {
10262
10433
  type: "button",
10263
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",
10264
10435
  onClick: (event) => handleViewChange(),
10265
- title: currentViewMode === "grid" ? getLabel59("switchToList") : getLabel59("switchToGrid"),
10436
+ title: currentViewMode === "grid" ? getLabel61("switchToList") : getLabel61("switchToGrid"),
10266
10437
  children: [
10267
10438
  currentViewMode === "grid" ? /* @__PURE__ */ jsxRuntime.jsxs(
10268
10439
  "svg",
@@ -10320,7 +10491,7 @@ function GridToolbar(rawProps) {
10320
10491
  onClick: (event) => {
10321
10492
  if (props.onClearFilters) props.onClearFilters();
10322
10493
  },
10323
- children: getLabel59("clearAll")
10494
+ children: getLabel61("clearAll")
10324
10495
  }
10325
10496
  ),
10326
10497
  props.priceFilterMin !== void 0 || props.priceFilterMax !== void 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
@@ -10331,7 +10502,7 @@ function GridToolbar(rawProps) {
10331
10502
  if (props.onPriceFilterRemove) props.onPriceFilterRemove();
10332
10503
  },
10333
10504
  children: [
10334
- getLabel59("price"),
10505
+ getLabel61("price"),
10335
10506
  ": ",
10336
10507
  currencySymbol,
10337
10508
  props.priceFilterMin ?? 0,
@@ -10383,7 +10554,7 @@ var DEFAULT_LABELS2 = {
10383
10554
  of: "of"
10384
10555
  };
10385
10556
  function GridPagination(props) {
10386
- function getLabel59(key) {
10557
+ function getLabel61(key) {
10387
10558
  const labels = props.labels || {};
10388
10559
  return labels[key] !== void 0 ? labels[key] : DEFAULT_LABELS2[key] || key;
10389
10560
  }
@@ -10456,15 +10627,15 @@ function GridPagination(props) {
10456
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",
10457
10628
  disabled: currentPage === 1,
10458
10629
  onClick: () => handlePageChange(currentPage - 1),
10459
- children: getLabel59("previous")
10630
+ children: getLabel61("previous")
10460
10631
  }
10461
10632
  ),
10462
10633
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "propeller-grid-pagination__info px-2 text-sm font-medium text-muted-foreground", children: [
10463
- getLabel59("page"),
10634
+ getLabel61("page"),
10464
10635
  "\xA0",
10465
10636
  currentPage,
10466
10637
  "\xA0",
10467
- getLabel59("of"),
10638
+ getLabel61("of"),
10468
10639
  "\xA0",
10469
10640
  totalPages
10470
10641
  ] }),
@@ -10475,7 +10646,7 @@ function GridPagination(props) {
10475
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",
10476
10647
  disabled: currentPage === totalPages,
10477
10648
  onClick: () => handlePageChange(currentPage + 1),
10478
- children: getLabel59("next")
10649
+ children: getLabel61("next")
10479
10650
  }
10480
10651
  )
10481
10652
  ] }) : null,
@@ -10487,7 +10658,7 @@ function GridPagination(props) {
10487
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",
10488
10659
  disabled: currentPage === 1,
10489
10660
  onClick: () => handlePageChange(currentPage - 1),
10490
- children: getLabel59("previous")
10661
+ children: getLabel61("previous")
10491
10662
  }
10492
10663
  ),
10493
10664
  getFullPages().map((item, idx) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -10514,7 +10685,7 @@ function GridPagination(props) {
10514
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",
10515
10686
  disabled: currentPage === totalPages,
10516
10687
  onClick: () => handlePageChange(currentPage + 1),
10517
- children: getLabel59("next")
10688
+ children: getLabel61("next")
10518
10689
  }
10519
10690
  )
10520
10691
  ] }) : null
@@ -11817,7 +11988,7 @@ function FavoriteListDetails(rawProps) {
11817
11988
  graphqlClient: props.graphqlClient,
11818
11989
  user: props.user ?? null
11819
11990
  });
11820
- const { addItem } = useCart({
11991
+ const { addItem: addItem2 } = useCart({
11821
11992
  graphqlClient: props.graphqlClient,
11822
11993
  user: props.user ?? null,
11823
11994
  cartId: props.cartId,
@@ -11922,7 +12093,7 @@ function FavoriteListDetails(rawProps) {
11922
12093
  setBulkBusy(true);
11923
12094
  try {
11924
12095
  for (const product of products) {
11925
- await addItem({
12096
+ await addItem2({
11926
12097
  product,
11927
12098
  quantity: product.minimumQuantity && product.minimumQuantity > 0 ? product.minimumQuantity : 1,
11928
12099
  cartId: props.cartId,
@@ -14388,6 +14559,130 @@ function OrderList(rawProps) {
14388
14559
  ] });
14389
14560
  }
14390
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;
14391
14686
  var ROW_SEQ = 0;
14392
14687
  function blankRow() {
14393
14688
  ROW_SEQ += 1;
@@ -14479,6 +14774,11 @@ function QuickOrderInner(props) {
14479
14774
  patchRow(key, { code: "", matches: [], productId: null, name: "", netPrice: 0, grossPrice: 0, searched: false });
14480
14775
  return;
14481
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
+ }
14482
14782
  patchRow(key, {
14483
14783
  code: match.sku,
14484
14784
  productId: match.productId,
@@ -14539,6 +14839,10 @@ function QuickOrderInner(props) {
14539
14839
  continue;
14540
14840
  }
14541
14841
  if (resolved.some((r) => r.productId === exact.productId)) continue;
14842
+ if (exact.priceOnRequest) {
14843
+ notFound.push(line.code);
14844
+ continue;
14845
+ }
14542
14846
  const row = blankRow();
14543
14847
  resolved.push({
14544
14848
  ...row,
@@ -16328,6 +16632,20 @@ function ProductInfoAddToCart(props = {}) {
16328
16632
  }
16329
16633
  );
16330
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
+ }
16331
16649
  const Injected = resolved.addToCartComponent;
16332
16650
  if (Injected) {
16333
16651
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -18892,7 +19210,8 @@ function mapToSearchBarResult(item, language, urls) {
18892
19210
  priceGross,
18893
19211
  imageUrl: displayItem?.media?.images?.items?.[0]?.imageVariants?.[0]?.url || "",
18894
19212
  url,
18895
- isCluster
19213
+ isCluster,
19214
+ priceOnRequest: isPriceOnRequest(item)
18896
19215
  };
18897
19216
  }
18898
19217
  function SearchBar(rawProps) {
@@ -19076,7 +19395,7 @@ function SearchBar(rawProps) {
19076
19395
  result.sku
19077
19396
  ] }) : null
19078
19397
  ] }),
19079
- 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: [
19080
19399
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-search-bar__result-price-value", children: formatItemPrice(leadingPrice(result)) }),
19081
19400
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "propeller-search-bar__result-price-label block text-xs font-normal text-muted-foreground", children: priceTaxLabel() })
19082
19401
  ] }) : null
@@ -19284,6 +19603,8 @@ exports.OrderList = OrderList_default;
19284
19603
  exports.OrderShipments = OrderShipments_default;
19285
19604
  exports.OrderSummary = OrderSummaryWithProvider_default;
19286
19605
  exports.OrderTotals = OrderTotalsWithProvider_default;
19606
+ exports.PRICE_REQUEST_STORAGE_KEY = PRICE_REQUEST_STORAGE_KEY;
19607
+ exports.PriceRequestList = PriceRequestList_default;
19287
19608
  exports.PriceToggle = PriceToggle_default;
19288
19609
  exports.ProductBulkPrices = ProductBulkPricesWithProvider_default;
19289
19610
  exports.ProductBundles = ProductBundles_default;
@@ -19307,12 +19628,18 @@ exports.PurchaseAuthorizationRequests = PurchaseAuthorizationRequests_default;
19307
19628
  exports.QuickOrder = QuickOrder;
19308
19629
  exports.QuoteActions = QuoteActions_default;
19309
19630
  exports.RegisterForm = RegisterForm_default;
19631
+ exports.RequestPriceButton = RequestPriceButton_default;
19310
19632
  exports.SearchBar = SearchBar_default;
19311
19633
  exports.UserDetails = UserDetails_default;
19634
+ exports.addPriceRequestItem = addItem;
19312
19635
  exports.buildRootMachinesQuery = buildRootMachinesQuery;
19313
19636
  exports.fetchActiveCart = fetchActiveCart;
19314
19637
  exports.initCart = initCart;
19638
+ exports.isPriceOnRequest = isPriceOnRequest;
19315
19639
  exports.mergeAnonymousCart = mergeAnonymousCart;
19640
+ exports.priceRequestContains = containsItem;
19641
+ exports.removePriceRequestItem = removeItem;
19642
+ exports.setPriceRequestItemQuantity = setItemQuantity;
19316
19643
  exports.useAddress = useAddress;
19317
19644
  exports.useAuth = useAuth;
19318
19645
  exports.useCart = useCart;
@@ -19324,6 +19651,7 @@ exports.useInfraProps = useInfraProps;
19324
19651
  exports.useMachines = useMachines;
19325
19652
  exports.useMenu = useMenu;
19326
19653
  exports.useOrders = useOrders;
19654
+ exports.usePriceRequest = usePriceRequest;
19327
19655
  exports.useProductBundles = useProductBundles;
19328
19656
  exports.useProductGridConfig = useProductGridConfig;
19329
19657
  exports.useProductInfo = useProductInfo;