@propeller-commerce/propeller-v2-react-ui 0.12.0 → 0.14.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.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _propeller_commerce_propeller_v2_core_ui from '@propeller-commerce/propeller-v2-core-ui';
2
- import { Services, ShopMode, UserMode, PriceComponentProps, StockComponentProps, AddToCartComponentProps, ImageComponentProps, BadgesComponentProps, FavoriteComponentProps, ProductBundlesComponentProps, ProductBulkPricesComponentProps, ProductSurchargesComponentProps, AnyUser, Result } from '@propeller-commerce/propeller-v2-core-ui';
2
+ import { Services, ShopMode, UserMode, PriceComponentProps, StockComponentProps, AddToCartComponentProps, ImageComponentProps, BadgesComponentProps, FavoriteComponentProps, ProductBundlesComponentProps, ProductBulkPricesComponentProps, ProductSurchargesComponentProps, AnyUser, Result, Availability } from '@propeller-commerce/propeller-v2-core-ui';
3
3
  export * from '@propeller-commerce/propeller-v2-core-ui';
4
4
  import * as React$1 from 'react';
5
5
  import React__default, { ReactNode, ComponentType } from 'react';
@@ -1395,6 +1395,12 @@ interface UseProductSearchOptions {
1395
1395
  priceFilterMin?: number;
1396
1396
  /** Upper price bound filter. */
1397
1397
  priceFilterMax?: number;
1398
+ /**
1399
+ * Stock buckets to filter by. Empty, unset, or both buckets sends no stock
1400
+ * filter. Filtering happens server-side, so `itemsFound` and the page count
1401
+ * describe the filtered set.
1402
+ */
1403
+ availability?: Availability[];
1398
1404
  /** Sort field; overrides the internal sort state when set. */
1399
1405
  sortField?: string;
1400
1406
  /** Sort direction (`'ASC'` / `'DESC'`); overrides internal state when set. */
@@ -4618,6 +4624,23 @@ interface GridFiltersProps {
4618
4624
  activePriceMin?: number;
4619
4625
  /** Currently active price filter max from URL state. */
4620
4626
  activePriceMax?: number;
4627
+ /**
4628
+ * Show the availability (stock) section. Defaults to false so hosts opt in
4629
+ * rather than gaining a new filter section on upgrade.
4630
+ *
4631
+ * The host should pass its own "show stock" setting here — a stock filter
4632
+ * is meaningless when no stock is displayed anywhere in the grid. When
4633
+ * truthy the section is still subject to the same semi-closed-portal
4634
+ * visibility rule as the price filter (hidden for anonymous users).
4635
+ */
4636
+ showAvailabilityFilter?: boolean;
4637
+ /**
4638
+ * Currently active availability buckets (URL-driven). Seeds the checkboxes
4639
+ * on the first render so a restored filtered URL renders them ticked.
4640
+ */
4641
+ activeAvailability?: Availability[];
4642
+ /** Called whenever an availability checkbox is toggled. */
4643
+ onAvailabilityChange?: (sel: Availability[]) => void;
4621
4644
  /**
4622
4645
  * When true, all checkboxes and price inputs are disabled.
4623
4646
  * Wire to ProductGrid's `onLoadingChange` to block rapid re-clicks while a fetch is in flight.
@@ -4802,6 +4825,16 @@ interface GridToolbarProps {
4802
4825
  * Called when the price filter badge × is clicked.
4803
4826
  */
4804
4827
  onPriceFilterRemove?: () => void;
4828
+ /**
4829
+ * Currently active availability (stock) filter selections.
4830
+ * Renders one removable chip per selected bucket.
4831
+ */
4832
+ availability?: Availability[];
4833
+ /**
4834
+ * Called when an availability filter badge × is clicked.
4835
+ * Receives the specific bucket to remove.
4836
+ */
4837
+ onAvailabilityFilterRemove?: (value: Availability) => void;
4805
4838
  /**
4806
4839
  * Called when "Clear All" is clicked.
4807
4840
  */
@@ -5245,6 +5278,10 @@ interface OrderListProps {
5245
5278
  showCompanyOrders?: boolean;
5246
5279
  /** Hide pagination controls. Defaults to false. */
5247
5280
  hidePagination?: boolean;
5281
+ /** Hide the column header row. Defaults to false. */
5282
+ hideHeader?: boolean;
5283
+ /** Drop the results container's card chrome (background/border/shadow). Defaults to false. */
5284
+ flat?: boolean;
5248
5285
  /** Filter orders by channel IDs */
5249
5286
  channelIds?: number[];
5250
5287
  /** Format price */
@@ -6195,6 +6232,11 @@ interface ProductGridProps {
6195
6232
  /** * Active text filters to apply — built by the parent from FiltersSidebar * `onFilterChange` callbacks. Each entry maps to a `textFilters` input * row in the CategoryService query. * When this prop changes the grid automatically re-fetches (page resets to 1). */ textFilters?: ProductTextFilterInput[];
6196
6233
  /** * Active price range lower bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMin?: number;
6197
6234
  /** * Active price range upper bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMax?: number;
6235
+ /**
6236
+ * Active stock buckets from the filters sidebar. Triggers a re-fetch when
6237
+ * changed. Empty or both buckets means no stock filter.
6238
+ */
6239
+ availability?: Availability[];
6198
6240
  /** * Called when sort state changes internally (for syncing a sibling toolbar). */ onSortChange?: (field: string, order: string) => void;
6199
6241
  /** * Called after each internal data fetch with the min/max price of the * current product set — use to populate a price range slider in the parent. */ onPriceBoundsChange?: (min: number, max: number) => void;
6200
6242
  /** * Called after each fetch with the total number of products found — * use to display a result count in the parent toolbar. */ onItemsFoundChange?: (count: number) => void;
@@ -7010,6 +7052,20 @@ interface PurchaseAuthorizationRequestsProps {
7010
7052
  user?: Contact | Customer;
7011
7053
  /** The companyId of the current selected company. Resolved from PropellerProvider when omitted. */
7012
7054
  companyId?: number;
7055
+ /** Limit the number of requests shown (e.g. 3 = last 3 modified). undefined = show all */
7056
+ limit?: number;
7057
+ /** Columns to display, in order. Keys: 'date' | 'quantity' | 'total' | 'requestedBy' | 'action'. Defaults to all five. */
7058
+ columns?: string[];
7059
+ /** Label mapping for columns */
7060
+ columnConfig?: Record<string, string>;
7061
+ /** Show the actions column and its buttons. Defaults to true. */
7062
+ showActions?: boolean;
7063
+ /** Hide the column header row. Defaults to false. */
7064
+ hideHeader?: boolean;
7065
+ /** Drop the results container's card chrome (background/border/shadow). Defaults to false. */
7066
+ flat?: boolean;
7067
+ /** Hide the component's own title. Defaults to false. */
7068
+ hideTitle?: boolean;
7013
7069
  /**
7014
7070
  * Override: fires instead of the default CartService.acceptPurchaseAuthorizationRequest() call.
7015
7071
  * Receives the cartId string.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _propeller_commerce_propeller_v2_core_ui from '@propeller-commerce/propeller-v2-core-ui';
2
- import { Services, ShopMode, UserMode, PriceComponentProps, StockComponentProps, AddToCartComponentProps, ImageComponentProps, BadgesComponentProps, FavoriteComponentProps, ProductBundlesComponentProps, ProductBulkPricesComponentProps, ProductSurchargesComponentProps, AnyUser, Result } from '@propeller-commerce/propeller-v2-core-ui';
2
+ import { Services, ShopMode, UserMode, PriceComponentProps, StockComponentProps, AddToCartComponentProps, ImageComponentProps, BadgesComponentProps, FavoriteComponentProps, ProductBundlesComponentProps, ProductBulkPricesComponentProps, ProductSurchargesComponentProps, AnyUser, Result, Availability } from '@propeller-commerce/propeller-v2-core-ui';
3
3
  export * from '@propeller-commerce/propeller-v2-core-ui';
4
4
  import * as React$1 from 'react';
5
5
  import React__default, { ReactNode, ComponentType } from 'react';
@@ -1395,6 +1395,12 @@ interface UseProductSearchOptions {
1395
1395
  priceFilterMin?: number;
1396
1396
  /** Upper price bound filter. */
1397
1397
  priceFilterMax?: number;
1398
+ /**
1399
+ * Stock buckets to filter by. Empty, unset, or both buckets sends no stock
1400
+ * filter. Filtering happens server-side, so `itemsFound` and the page count
1401
+ * describe the filtered set.
1402
+ */
1403
+ availability?: Availability[];
1398
1404
  /** Sort field; overrides the internal sort state when set. */
1399
1405
  sortField?: string;
1400
1406
  /** Sort direction (`'ASC'` / `'DESC'`); overrides internal state when set. */
@@ -4618,6 +4624,23 @@ interface GridFiltersProps {
4618
4624
  activePriceMin?: number;
4619
4625
  /** Currently active price filter max from URL state. */
4620
4626
  activePriceMax?: number;
4627
+ /**
4628
+ * Show the availability (stock) section. Defaults to false so hosts opt in
4629
+ * rather than gaining a new filter section on upgrade.
4630
+ *
4631
+ * The host should pass its own "show stock" setting here — a stock filter
4632
+ * is meaningless when no stock is displayed anywhere in the grid. When
4633
+ * truthy the section is still subject to the same semi-closed-portal
4634
+ * visibility rule as the price filter (hidden for anonymous users).
4635
+ */
4636
+ showAvailabilityFilter?: boolean;
4637
+ /**
4638
+ * Currently active availability buckets (URL-driven). Seeds the checkboxes
4639
+ * on the first render so a restored filtered URL renders them ticked.
4640
+ */
4641
+ activeAvailability?: Availability[];
4642
+ /** Called whenever an availability checkbox is toggled. */
4643
+ onAvailabilityChange?: (sel: Availability[]) => void;
4621
4644
  /**
4622
4645
  * When true, all checkboxes and price inputs are disabled.
4623
4646
  * Wire to ProductGrid's `onLoadingChange` to block rapid re-clicks while a fetch is in flight.
@@ -4802,6 +4825,16 @@ interface GridToolbarProps {
4802
4825
  * Called when the price filter badge × is clicked.
4803
4826
  */
4804
4827
  onPriceFilterRemove?: () => void;
4828
+ /**
4829
+ * Currently active availability (stock) filter selections.
4830
+ * Renders one removable chip per selected bucket.
4831
+ */
4832
+ availability?: Availability[];
4833
+ /**
4834
+ * Called when an availability filter badge × is clicked.
4835
+ * Receives the specific bucket to remove.
4836
+ */
4837
+ onAvailabilityFilterRemove?: (value: Availability) => void;
4805
4838
  /**
4806
4839
  * Called when "Clear All" is clicked.
4807
4840
  */
@@ -5245,6 +5278,10 @@ interface OrderListProps {
5245
5278
  showCompanyOrders?: boolean;
5246
5279
  /** Hide pagination controls. Defaults to false. */
5247
5280
  hidePagination?: boolean;
5281
+ /** Hide the column header row. Defaults to false. */
5282
+ hideHeader?: boolean;
5283
+ /** Drop the results container's card chrome (background/border/shadow). Defaults to false. */
5284
+ flat?: boolean;
5248
5285
  /** Filter orders by channel IDs */
5249
5286
  channelIds?: number[];
5250
5287
  /** Format price */
@@ -6195,6 +6232,11 @@ interface ProductGridProps {
6195
6232
  /** * Active text filters to apply — built by the parent from FiltersSidebar * `onFilterChange` callbacks. Each entry maps to a `textFilters` input * row in the CategoryService query. * When this prop changes the grid automatically re-fetches (page resets to 1). */ textFilters?: ProductTextFilterInput[];
6196
6233
  /** * Active price range lower bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMin?: number;
6197
6234
  /** * Active price range upper bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMax?: number;
6235
+ /**
6236
+ * Active stock buckets from the filters sidebar. Triggers a re-fetch when
6237
+ * changed. Empty or both buckets means no stock filter.
6238
+ */
6239
+ availability?: Availability[];
6198
6240
  /** * Called when sort state changes internally (for syncing a sibling toolbar). */ onSortChange?: (field: string, order: string) => void;
6199
6241
  /** * Called after each internal data fetch with the min/max price of the * current product set — use to populate a price range slider in the parent. */ onPriceBoundsChange?: (min: number, max: number) => void;
6200
6242
  /** * Called after each fetch with the total number of products found — * use to display a result count in the parent toolbar. */ onItemsFoundChange?: (count: number) => void;
@@ -7010,6 +7052,20 @@ interface PurchaseAuthorizationRequestsProps {
7010
7052
  user?: Contact | Customer;
7011
7053
  /** The companyId of the current selected company. Resolved from PropellerProvider when omitted. */
7012
7054
  companyId?: number;
7055
+ /** Limit the number of requests shown (e.g. 3 = last 3 modified). undefined = show all */
7056
+ limit?: number;
7057
+ /** Columns to display, in order. Keys: 'date' | 'quantity' | 'total' | 'requestedBy' | 'action'. Defaults to all five. */
7058
+ columns?: string[];
7059
+ /** Label mapping for columns */
7060
+ columnConfig?: Record<string, string>;
7061
+ /** Show the actions column and its buttons. Defaults to true. */
7062
+ showActions?: boolean;
7063
+ /** Hide the column header row. Defaults to false. */
7064
+ hideHeader?: boolean;
7065
+ /** Drop the results container's card chrome (background/border/shadow). Defaults to false. */
7066
+ flat?: boolean;
7067
+ /** Hide the component's own title. Defaults to false. */
7068
+ hideTitle?: boolean;
7013
7069
  /**
7014
7070
  * Override: fires instead of the default CartService.acceptPurchaseAuthorizationRequest() call.
7015
7071
  * Receives the cartId string.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { getLabel, getLocalizedValue, formatPrice, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, getLanguageString, collectAttributeValues, getCountryName } from '@propeller-commerce/propeller-v2-core-ui';
2
+ import { getLabel, getLocalizedValue, formatPrice, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, getLanguageString, collectAttributeValues, getCountryName } 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
5
  import { createContext, useState, useRef, useMemo, useCallback, useContext, useEffect } from 'react';
@@ -2241,7 +2241,8 @@ function useProductSearch(options) {
2241
2241
  userKey,
2242
2242
  JSON.stringify(options.textFilters ?? []),
2243
2243
  options.priceFilterMin ?? "",
2244
- options.priceFilterMax ?? ""
2244
+ options.priceFilterMax ?? "",
2245
+ JSON.stringify(options.availability ?? [])
2245
2246
  ].join("|");
2246
2247
  if (inflightFetches2.has(inflightKey)) {
2247
2248
  fetchIdRef.current--;
@@ -2283,6 +2284,7 @@ function useProductSearch(options) {
2283
2284
  }
2284
2285
  ] : [];
2285
2286
  const priceFilter = options.priceFilterMin !== void 0 || options.priceFilterMax !== void 0 ? { from: options.priceFilterMin ?? 0, to: options.priceFilterMax ?? 999999 } : void 0;
2287
+ const inventoryFilter = buildInventoryFilter(options.availability);
2286
2288
  const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
2287
2289
  const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
2288
2290
  const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
@@ -2305,6 +2307,7 @@ function useProductSearch(options) {
2305
2307
  ...options.brand && { manufacturers: [options.brand] },
2306
2308
  ...options.textFilters?.length && { textFilters: options.textFilters },
2307
2309
  ...priceFilter && { price: priceFilter },
2310
+ ...inventoryFilter && { inventory: inventoryFilter },
2308
2311
  ...sortInputs.length && { sortInputs },
2309
2312
  ...options.companyId && { companyId: options.companyId },
2310
2313
  ...userId !== void 0 && { userId },
@@ -2381,6 +2384,7 @@ function useProductSearch(options) {
2381
2384
  options.textFilters,
2382
2385
  options.priceFilterMin,
2383
2386
  options.priceFilterMax,
2387
+ options.availability,
2384
2388
  options.sortField,
2385
2389
  options.sortOrder,
2386
2390
  options.companyId,
@@ -2420,6 +2424,7 @@ function useProductSearch(options) {
2420
2424
  const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
2421
2425
  const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
2422
2426
  const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
2427
+ const inventoryFilter = buildInventoryFilter(options.availability);
2423
2428
  const categoryProductSearchInput = {
2424
2429
  language,
2425
2430
  page: 1,
@@ -2431,6 +2436,7 @@ function useProductSearch(options) {
2431
2436
  ProductStatus.S
2432
2437
  ],
2433
2438
  hidden: false,
2439
+ ...inventoryFilter && { inventory: inventoryFilter },
2434
2440
  term,
2435
2441
  searchFields: [
2436
2442
  {
@@ -2510,6 +2516,7 @@ function useProductSearch(options) {
2510
2516
  JSON.stringify(options.textFilters),
2511
2517
  options.priceFilterMin,
2512
2518
  options.priceFilterMax,
2519
+ options.availability,
2513
2520
  options.sortField,
2514
2521
  options.sortOrder,
2515
2522
  pageSize,
@@ -9032,6 +9039,7 @@ function ProductGrid(rawProps) {
9032
9039
  textFilters: props.textFilters,
9033
9040
  priceFilterMin: props.priceFilterMin,
9034
9041
  priceFilterMax: props.priceFilterMax,
9042
+ availability: props.availability,
9035
9043
  sortField: props.sortField,
9036
9044
  sortOrder: props.sortOrder,
9037
9045
  pageSize: props.pageSize,
@@ -9334,11 +9342,18 @@ function GridFilters(rawProps) {
9334
9342
  const [isPending, setIsPending] = useState(() => false);
9335
9343
  const [appliedMin, setAppliedMin] = useState(() => void 0);
9336
9344
  const [appliedMax, setAppliedMax] = useState(() => void 0);
9345
+ const [availability, setAvailability] = useState(
9346
+ () => [...props.activeAvailability || []]
9347
+ );
9337
9348
  function showPriceFilter() {
9338
9349
  const mode = props.portalMode || "open";
9339
9350
  if (mode === "open") return true;
9340
9351
  return !!props.user;
9341
9352
  }
9353
+ function showAvailability() {
9354
+ if (!props.showAvailabilityFilter) return false;
9355
+ return !isContentHidden(props.portalMode, props.user);
9356
+ }
9342
9357
  function getFilterName(filter) {
9343
9358
  return filter?.attributeDescription?.name || "";
9344
9359
  }
@@ -9383,6 +9398,13 @@ function GridFilters(rawProps) {
9383
9398
  props.onFilterChange(filter, value);
9384
9399
  if (props.getSelectedFilters) props.getSelectedFilters();
9385
9400
  }
9401
+ function handleAvailabilityToggle(value, checked) {
9402
+ const next = checked ? [...availability, value] : availability.filter((v) => v !== value);
9403
+ setAvailability(next);
9404
+ setIsPending(true);
9405
+ if (props.onAvailabilityChange) props.onAvailabilityChange(next);
9406
+ if (props.getSelectedFilters) props.getSelectedFilters();
9407
+ }
9386
9408
  function handleMinChange(value) {
9387
9409
  const n = value > currentMax ? currentMax : value;
9388
9410
  setCurrentMin(n);
@@ -9456,11 +9478,16 @@ function GridFilters(rawProps) {
9456
9478
  setCurrentMin(props.priceMin || 0);
9457
9479
  setCurrentMax(props.priceMax || 9999);
9458
9480
  setExpandedFilters({});
9481
+ setAvailability([]);
9459
9482
  }, [props.clearSignal, props.priceMin, props.priceMax]);
9460
9483
  useEffect(() => {
9461
9484
  if (!props.activeTextFilters) return;
9462
9485
  setSelectedFilters(props.activeTextFilters);
9463
9486
  }, [props.activeTextFilters]);
9487
+ useEffect(() => {
9488
+ if (!props.activeAvailability) return;
9489
+ setAvailability([...props.activeAvailability]);
9490
+ }, [props.activeAvailability]);
9464
9491
  useEffect(() => {
9465
9492
  if (props.activePriceMin === void 0 && props.activePriceMax === void 0) {
9466
9493
  setCurrentMin(props.priceMin || 0);
@@ -9560,6 +9587,40 @@ function GridFilters(rawProps) {
9560
9587
  " ",
9561
9588
  /* @__PURE__ */ jsx("div", { className: "propeller-grid-filters__divider h-px bg-border-subtle" })
9562
9589
  ] }) : null,
9590
+ showAvailability() ? /* @__PURE__ */ jsxs(Fragment, { children: [
9591
+ /* @__PURE__ */ jsxs("div", { className: "propeller-grid-filters__availability space-y-3", children: [
9592
+ /* @__PURE__ */ jsx("h3", { className: "propeller-grid-filters__availability-title text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: getLabel(props.labels, "availability", "Availability") }),
9593
+ /* @__PURE__ */ jsxs("div", { className: "propeller-grid-filters__availability-options space-y-1.5", children: [
9594
+ /* @__PURE__ */ jsxs("label", { className: "propeller-grid-filters__option flex items-center gap-2 cursor-pointer group", children: [
9595
+ /* @__PURE__ */ jsx(
9596
+ "input",
9597
+ {
9598
+ type: "checkbox",
9599
+ className: "propeller-grid-filters__checkbox h-4 w-4 rounded border-input text-secondary focus:ring-secondary cursor-pointer flex-shrink-0",
9600
+ checked: availability.includes("in-stock"),
9601
+ disabled: !!props.isLoading,
9602
+ onChange: (e) => handleAvailabilityToggle("in-stock", e.target.checked)
9603
+ }
9604
+ ),
9605
+ /* @__PURE__ */ jsx("span", { className: "propeller-grid-filters__option-label flex-1 text-sm text-muted-foreground leading-none select-none group-hover:text-foreground", children: getLabel(props.labels, "inStock", "In stock") })
9606
+ ] }),
9607
+ /* @__PURE__ */ jsxs("label", { className: "propeller-grid-filters__option flex items-center gap-2 cursor-pointer group", children: [
9608
+ /* @__PURE__ */ jsx(
9609
+ "input",
9610
+ {
9611
+ type: "checkbox",
9612
+ className: "propeller-grid-filters__checkbox h-4 w-4 rounded border-input text-secondary focus:ring-secondary cursor-pointer flex-shrink-0",
9613
+ checked: availability.includes("out-of-stock"),
9614
+ disabled: !!props.isLoading,
9615
+ onChange: (e) => handleAvailabilityToggle("out-of-stock", e.target.checked)
9616
+ }
9617
+ ),
9618
+ /* @__PURE__ */ jsx("span", { className: "propeller-grid-filters__option-label flex-1 text-sm text-muted-foreground leading-none select-none group-hover:text-foreground", children: getLabel(props.labels, "outOfStock", "Out of stock") })
9619
+ ] })
9620
+ ] })
9621
+ ] }),
9622
+ /* @__PURE__ */ jsx("div", { className: "propeller-grid-filters__divider h-px bg-border-subtle" })
9623
+ ] }) : null,
9563
9624
  props.filters.length === 0 ? /* @__PURE__ */ jsx("p", { className: "propeller-grid-filters__empty text-sm text-foreground-subtle italic", children: getLabel(props.labels, "noFiltersAvailable", "No filters available") }) : null,
9564
9625
  getFilteredFilters()?.map((filter) => /* @__PURE__ */ jsxs("div", { className: "propeller-grid-filters__group border-b border-border-subtle pb-3 last:border-b-0", "data-expanded": isExpanded(getFilterName(filter)) ? "true" : "false", children: [
9565
9626
  /* @__PURE__ */ jsxs(
@@ -9735,6 +9796,8 @@ var DEFAULT_LABELS = {
9735
9796
  results: "results",
9736
9797
  perPage: " per page",
9737
9798
  price: "Price",
9799
+ inStock: "In stock",
9800
+ outOfStock: "Out of stock",
9738
9801
  switchToList: "Switch to list view",
9739
9802
  switchToGrid: "Switch to grid view"
9740
9803
  };
@@ -9763,7 +9826,8 @@ function GridToolbar(rawProps) {
9763
9826
  const text = props.activeTextFilters || {};
9764
9827
  const hasText = Object.keys(text).some((k) => (text[k] || []).length > 0);
9765
9828
  const hasPrice = props.priceFilterMin !== void 0 || props.priceFilterMax !== void 0;
9766
- return hasText || hasPrice;
9829
+ const hasAvailability = (props.availability || []).length > 0;
9830
+ return hasText || hasPrice || hasAvailability;
9767
9831
  }
9768
9832
  function getActiveFilterBadges() {
9769
9833
  const text = props.activeTextFilters || {};
@@ -9781,6 +9845,9 @@ function GridToolbar(rawProps) {
9781
9845
  function isPriceSortDisabled() {
9782
9846
  return isContentHidden(props.portalMode, props.user);
9783
9847
  }
9848
+ function getAvailabilityLabel(value) {
9849
+ return value === "in-stock" ? getLabel56("inStock") : getLabel56("outOfStock");
9850
+ }
9784
9851
  function handleSortFieldChange(field) {
9785
9852
  setCurrentSortField(field);
9786
9853
  if (props.onSortChange) props.onSortChange(field, currentSortOrder);
@@ -9954,6 +10021,20 @@ function GridToolbar(rawProps) {
9954
10021
  ]
9955
10022
  }
9956
10023
  ) : null,
10024
+ (props.availability || []).map((value) => /* @__PURE__ */ jsxs(
10025
+ "span",
10026
+ {
10027
+ className: "propeller-grid-toolbar__filter-badge propeller-grid-toolbar__filter-badge--availability inline-flex items-center gap-1 cursor-pointer px-2.5 py-0.5 rounded-full text-xs font-semibold border border-input bg-background hover:bg-primary hover:text-primary-foreground hover:border-primary transition-colors",
10028
+ onClick: (event) => {
10029
+ if (props.onAvailabilityFilterRemove) props.onAvailabilityFilterRemove(value);
10030
+ },
10031
+ children: [
10032
+ getAvailabilityLabel(value),
10033
+ /* @__PURE__ */ jsx("span", { className: "propeller-grid-toolbar__filter-badge-remove", children: "\xD7" })
10034
+ ]
10035
+ },
10036
+ value
10037
+ )),
9957
10038
  getActiveFilterBadges()?.map((badge) => /* @__PURE__ */ jsxs(
9958
10039
  "span",
9959
10040
  {
@@ -13805,9 +13886,9 @@ function OrderList(rawProps) {
13805
13886
  )
13806
13887
  ] })
13807
13888
  ] }) : null,
13808
- !loading || orders.length > 0 ? /* @__PURE__ */ jsx(Fragment, { children: orders.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "propeller-order-list__results bg-card rounded-container shadow overflow-hidden", children: [
13889
+ !loading || orders.length > 0 ? /* @__PURE__ */ jsx(Fragment, { children: orders.length > 0 ? /* @__PURE__ */ jsxs("div", { className: `propeller-order-list__results${props.flat ? "" : " bg-card rounded-container shadow"} overflow-hidden`, children: [
13809
13890
  /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "propeller-order-list__table min-w-full divide-y divide-border", children: [
13810
- /* @__PURE__ */ jsx("thead", { className: "propeller-order-list__thead bg-surface-hover", children: /* @__PURE__ */ jsx("tr", { children: columns?.map((col) => /* @__PURE__ */ jsx(
13891
+ !props.hideHeader ? /* @__PURE__ */ jsx("thead", { className: "propeller-order-list__thead bg-surface-hover", children: /* @__PURE__ */ jsx("tr", { children: columns?.map((col) => /* @__PURE__ */ jsx(
13811
13892
  "th",
13812
13893
  {
13813
13894
  "data-column": col,
@@ -13815,7 +13896,7 @@ function OrderList(rawProps) {
13815
13896
  children: getColumnLabel(col)
13816
13897
  },
13817
13898
  col
13818
- )) }) }),
13899
+ )) }) }) : null,
13819
13900
  /* @__PURE__ */ jsx("tbody", { className: "propeller-order-list__tbody bg-card divide-y divide-border", children: orders?.map((order) => /* @__PURE__ */ jsx(
13820
13901
  "tr",
13821
13902
  {
@@ -13922,7 +14003,7 @@ function OrderList(rawProps) {
13922
14003
  ) })
13923
14004
  ] })
13924
14005
  ] }) : null
13925
- ] }) : /* @__PURE__ */ jsx("div", { className: "propeller-order-list__empty bg-card rounded-container shadow p-8 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mb-4", children: getLabel(props.labels, "noOrders", "No orders found.") }) }) }) : /* @__PURE__ */ jsx("div", { className: "propeller-order-list__loading p-8 text-center text-muted-foreground", children: getLabel(props.labels, "loading", "Loading orders...") })
14006
+ ] }) : /* @__PURE__ */ jsx("div", { className: `propeller-order-list__empty${props.flat ? "" : " bg-card rounded-container shadow"} p-8 text-center`, children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mb-4", children: getLabel(props.labels, "noOrders", "No orders found.") }) }) }) : /* @__PURE__ */ jsx("div", { className: "propeller-order-list__loading p-8 text-center text-muted-foreground", children: getLabel(props.labels, "loading", "Loading orders...") })
13926
14007
  ] });
13927
14008
  }
13928
14009
  var OrderList_default = OrderList;
@@ -17089,6 +17170,10 @@ function PurchaseAuthorizationConfigurator(rawProps) {
17089
17170
  var PurchaseAuthorizationConfigurator_default = PurchaseAuthorizationConfigurator;
17090
17171
  function PurchaseAuthorizationRequests(rawProps) {
17091
17172
  const props = useInfraProps(rawProps);
17173
+ const showActions = props.showActions !== false;
17174
+ const columns = (props.columns || ["date", "quantity", "total", "requestedBy", "action"]).filter(
17175
+ (col) => showActions || col !== "action"
17176
+ );
17092
17177
  const {
17093
17178
  carts,
17094
17179
  loading,
@@ -17122,6 +17207,17 @@ function PurchaseAuthorizationRequests(rawProps) {
17122
17207
  await handleDeleteRequest();
17123
17208
  setShowDeleteConfirm(false);
17124
17209
  };
17210
+ const displayedCarts = useMemo(() => {
17211
+ if (props.limit && props.limit > 0) {
17212
+ const sorted = [...carts].sort((a, b) => {
17213
+ const dateA = new Date(a.lastModifiedAt || "").getTime();
17214
+ const dateB = new Date(b.lastModifiedAt || "").getTime();
17215
+ return dateB - dateA;
17216
+ });
17217
+ return sorted.slice(0, props.limit);
17218
+ }
17219
+ return carts;
17220
+ }, [carts, props.limit]);
17125
17221
  function formatDate(dateStr) {
17126
17222
  if (props.formatDate) return props.formatDate(dateStr);
17127
17223
  if (!dateStr) return "-";
@@ -17135,6 +17231,18 @@ function PurchaseAuthorizationRequests(rawProps) {
17135
17231
  if (props.formatPrice) return props.formatPrice(price);
17136
17232
  return formatPrice(price, { symbol: props.currency ?? "\u20AC" });
17137
17233
  }
17234
+ const columnLabelKeys = {
17235
+ date: ["colDate", "Date"],
17236
+ quantity: ["colQuantity", "Quantity"],
17237
+ total: ["colTotal", "Total"],
17238
+ requestedBy: ["colRequestedBy", "Requested by"],
17239
+ action: ["colActions", "Actions"]
17240
+ };
17241
+ function getColumnLabel(col) {
17242
+ if (props.columnConfig?.[col]) return props.columnConfig[col];
17243
+ const [key, fallback] = columnLabelKeys[col] || [col, col];
17244
+ return getLabel(props.labels, key, fallback);
17245
+ }
17138
17246
  function getProductName5(item) {
17139
17247
  const lang = props.language || "NL";
17140
17248
  const fromNames = getLanguageString(item?.product?.names, lang, "");
@@ -17144,27 +17252,21 @@ function PurchaseAuthorizationRequests(rawProps) {
17144
17252
  return item?.product?.sku || "";
17145
17253
  }
17146
17254
  return /* @__PURE__ */ jsx("div", { className: `propeller-purchase-authorization-requests ${props.className || ""}`, children: isAuthManager ? /* @__PURE__ */ jsxs("div", { className: "propeller-purchase-authorization-requests__content space-y-4", children: [
17147
- /* @__PURE__ */ jsx("h2", { className: "propeller-purchase-authorization-requests__title text-xl font-semibold", children: getLabel(props.labels, "title", "Authorization Requests") }),
17255
+ !props.hideTitle ? /* @__PURE__ */ jsx("h2", { className: "propeller-purchase-authorization-requests__title text-xl font-semibold", children: getLabel(props.labels, "title", "Authorization Requests") }) : null,
17148
17256
  loading ? /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsx("div", { className: "w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin" }) }) : null,
17149
17257
  !loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
17150
17258
  carts.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-muted-foreground", children: getLabel(props.labels, "empty", "No pending authorization requests") }) : null,
17151
- carts.length > 0 ? /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border bg-card shadow-sm", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
17152
- /* @__PURE__ */ jsx("thead", { className: "bg-muted/50 border-b border-border", children: /* @__PURE__ */ jsxs("tr", { children: [
17153
- /* @__PURE__ */ jsx("th", { className: "text-left px-4 py-3 font-medium text-muted-foreground", children: getLabel(props.labels, "colDate", "Date") }),
17154
- /* @__PURE__ */ jsx("th", { className: "text-left px-4 py-3 font-medium text-muted-foreground", children: getLabel(props.labels, "colQuantity", "Quantity") }),
17155
- /* @__PURE__ */ jsx("th", { className: "text-left px-4 py-3 font-medium text-muted-foreground", children: getLabel(props.labels, "colTotal", "Total") }),
17156
- /* @__PURE__ */ jsx("th", { className: "text-left px-4 py-3 font-medium text-muted-foreground", children: getLabel(props.labels, "colRequestedBy", "Requested by") }),
17157
- /* @__PURE__ */ jsx("th", { className: "text-left px-4 py-3 font-medium text-muted-foreground", children: getLabel(props.labels, "colActions", "Actions") })
17158
- ] }) }),
17159
- /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-border", children: carts.map((cart, index) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-muted/30 transition-colors", children: [
17160
- /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-muted-foreground", children: formatDate(cart.lastModifiedAt ?? "") }),
17161
- /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: getTotalQuantity(cart) }),
17162
- /* @__PURE__ */ jsx("td", { className: "px-4 py-3 font-medium", children: formatPrice17(cart.total?.totalNet ?? 0) }),
17163
- /* @__PURE__ */ jsxs("td", { className: "px-4 py-3", children: [
17259
+ carts.length > 0 ? /* @__PURE__ */ jsx("div", { className: `propeller-purchase-authorization-requests__results overflow-x-auto${props.flat ? "" : " rounded-lg border border-border bg-card shadow-sm"}`, children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
17260
+ !props.hideHeader ? /* @__PURE__ */ jsx("thead", { className: "bg-muted/50 border-b border-border", children: /* @__PURE__ */ jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsx("th", { className: "text-left px-4 py-3 font-medium text-muted-foreground", children: getColumnLabel(col) }, col)) }) }) : null,
17261
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-border", children: displayedCarts.map((cart, index) => /* @__PURE__ */ jsx("tr", { className: "hover:bg-muted/30 transition-colors", children: columns.map((col) => /* @__PURE__ */ jsxs("td", { className: `px-4 py-3${col === "date" ? " text-muted-foreground" : ""}${col === "total" ? " font-medium" : ""}`, children: [
17262
+ col === "date" ? formatDate(cart.lastModifiedAt ?? "") : null,
17263
+ col === "quantity" ? getTotalQuantity(cart) : null,
17264
+ col === "total" ? formatPrice17(cart.total?.totalNet ?? 0) : null,
17265
+ col === "requestedBy" ? /* @__PURE__ */ jsxs(Fragment, { children: [
17164
17266
  /* @__PURE__ */ jsx("div", { className: "font-medium", children: getContactName(cart.contact) }),
17165
17267
  /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground mt-0.5", children: cart.contact?.email })
17166
- ] }),
17167
- /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(
17268
+ ] }) : null,
17269
+ col === "action" ? /* @__PURE__ */ jsx(
17168
17270
  "button",
17169
17271
  {
17170
17272
  type: "button",
@@ -17172,8 +17274,8 @@ function PurchaseAuthorizationRequests(rawProps) {
17172
17274
  onClick: () => handleViewCart(cart),
17173
17275
  children: getLabel(props.labels, "view", "View")
17174
17276
  }
17175
- ) })
17176
- ] }, index)) })
17277
+ ) : null
17278
+ ] }, col)) }, index)) })
17177
17279
  ] }) }) : null
17178
17280
  ] }) : null,
17179
17281
  selectedCart ? /* @__PURE__ */ jsxs("div", { className: "propeller-purchase-authorization-requests__modal fixed inset-0 z-50 flex items-center justify-center px-4", children: [