@propeller-commerce/propeller-v2-react-ui 0.11.4 → 0.13.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/CHANGELOG.md +65 -0
- package/dist/index.cjs +102 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -1
- package/dist/index.d.ts +54 -1
- package/dist/index.js +103 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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';
|
|
@@ -1383,12 +1383,24 @@ interface UseProductSearchOptions {
|
|
|
1383
1383
|
* without a contract still sees the full catalogue.
|
|
1384
1384
|
*/
|
|
1385
1385
|
applyOrderlists?: boolean;
|
|
1386
|
+
/**
|
|
1387
|
+
* Attribute names to request per product, e.g. `['MPN']`. Unset returns the
|
|
1388
|
+
* first page of ALL attributes (12 per product), so products with more than
|
|
1389
|
+
* 12 silently lose the rest — name what you render.
|
|
1390
|
+
*/
|
|
1391
|
+
productTrackAttributes?: string[];
|
|
1386
1392
|
/** Attribute text filters applied to the category search. */
|
|
1387
1393
|
textFilters?: ProductTextFilterInput[];
|
|
1388
1394
|
/** Lower price bound filter. */
|
|
1389
1395
|
priceFilterMin?: number;
|
|
1390
1396
|
/** Upper price bound filter. */
|
|
1391
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[];
|
|
1392
1404
|
/** Sort field; overrides the internal sort state when set. */
|
|
1393
1405
|
sortField?: string;
|
|
1394
1406
|
/** Sort direction (`'ASC'` / `'DESC'`); overrides internal state when set. */
|
|
@@ -4612,6 +4624,23 @@ interface GridFiltersProps {
|
|
|
4612
4624
|
activePriceMin?: number;
|
|
4613
4625
|
/** Currently active price filter max from URL state. */
|
|
4614
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;
|
|
4615
4644
|
/**
|
|
4616
4645
|
* When true, all checkboxes and price inputs are disabled.
|
|
4617
4646
|
* Wire to ProductGrid's `onLoadingChange` to block rapid re-clicks while a fetch is in flight.
|
|
@@ -4796,6 +4825,16 @@ interface GridToolbarProps {
|
|
|
4796
4825
|
* Called when the price filter badge × is clicked.
|
|
4797
4826
|
*/
|
|
4798
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;
|
|
4799
4838
|
/**
|
|
4800
4839
|
* Called when "Clear All" is clicked.
|
|
4801
4840
|
*/
|
|
@@ -6146,6 +6185,13 @@ interface ProductGridProps {
|
|
|
6146
6185
|
* still sees the full catalogue.
|
|
6147
6186
|
*/
|
|
6148
6187
|
applyOrderlists?: boolean;
|
|
6188
|
+
/**
|
|
6189
|
+
* Attribute names to request per product, e.g. `['MPN']` — makes
|
|
6190
|
+
* `product.attributes` usable in card slots like `belowName`. Unset returns
|
|
6191
|
+
* the first page of ALL attributes (12 per product), so products with more
|
|
6192
|
+
* than 12 silently lose the rest.
|
|
6193
|
+
*/
|
|
6194
|
+
productTrackAttributes?: string[];
|
|
6149
6195
|
/** Number of columns in the grid. Accepts 2, 3, 4, 5, or 6. Defaults to 3. */
|
|
6150
6196
|
columns?: number;
|
|
6151
6197
|
/**
|
|
@@ -6182,6 +6228,11 @@ interface ProductGridProps {
|
|
|
6182
6228
|
/** * 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[];
|
|
6183
6229
|
/** * Active price range lower bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMin?: number;
|
|
6184
6230
|
/** * Active price range upper bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMax?: number;
|
|
6231
|
+
/**
|
|
6232
|
+
* Active stock buckets from the filters sidebar. Triggers a re-fetch when
|
|
6233
|
+
* changed. Empty or both buckets means no stock filter.
|
|
6234
|
+
*/
|
|
6235
|
+
availability?: Availability[];
|
|
6185
6236
|
/** * Called when sort state changes internally (for syncing a sibling toolbar). */ onSortChange?: (field: string, order: string) => void;
|
|
6186
6237
|
/** * 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;
|
|
6187
6238
|
/** * 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;
|
|
@@ -7300,6 +7351,8 @@ interface SearchBarProps {
|
|
|
7300
7351
|
* still sees the full catalogue.
|
|
7301
7352
|
*/
|
|
7302
7353
|
applyOrderlists?: boolean;
|
|
7354
|
+
/** Attribute names to request per product, e.g. `['MPN']`. See ProductGrid. */
|
|
7355
|
+
productTrackAttributes?: string[];
|
|
7303
7356
|
/**
|
|
7304
7357
|
* Bump this counter to clear the search input from outside (e.g. on route
|
|
7305
7358
|
* change). Each unique value triggers a one-time reset of the local term.
|
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';
|
|
@@ -1383,12 +1383,24 @@ interface UseProductSearchOptions {
|
|
|
1383
1383
|
* without a contract still sees the full catalogue.
|
|
1384
1384
|
*/
|
|
1385
1385
|
applyOrderlists?: boolean;
|
|
1386
|
+
/**
|
|
1387
|
+
* Attribute names to request per product, e.g. `['MPN']`. Unset returns the
|
|
1388
|
+
* first page of ALL attributes (12 per product), so products with more than
|
|
1389
|
+
* 12 silently lose the rest — name what you render.
|
|
1390
|
+
*/
|
|
1391
|
+
productTrackAttributes?: string[];
|
|
1386
1392
|
/** Attribute text filters applied to the category search. */
|
|
1387
1393
|
textFilters?: ProductTextFilterInput[];
|
|
1388
1394
|
/** Lower price bound filter. */
|
|
1389
1395
|
priceFilterMin?: number;
|
|
1390
1396
|
/** Upper price bound filter. */
|
|
1391
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[];
|
|
1392
1404
|
/** Sort field; overrides the internal sort state when set. */
|
|
1393
1405
|
sortField?: string;
|
|
1394
1406
|
/** Sort direction (`'ASC'` / `'DESC'`); overrides internal state when set. */
|
|
@@ -4612,6 +4624,23 @@ interface GridFiltersProps {
|
|
|
4612
4624
|
activePriceMin?: number;
|
|
4613
4625
|
/** Currently active price filter max from URL state. */
|
|
4614
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;
|
|
4615
4644
|
/**
|
|
4616
4645
|
* When true, all checkboxes and price inputs are disabled.
|
|
4617
4646
|
* Wire to ProductGrid's `onLoadingChange` to block rapid re-clicks while a fetch is in flight.
|
|
@@ -4796,6 +4825,16 @@ interface GridToolbarProps {
|
|
|
4796
4825
|
* Called when the price filter badge × is clicked.
|
|
4797
4826
|
*/
|
|
4798
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;
|
|
4799
4838
|
/**
|
|
4800
4839
|
* Called when "Clear All" is clicked.
|
|
4801
4840
|
*/
|
|
@@ -6146,6 +6185,13 @@ interface ProductGridProps {
|
|
|
6146
6185
|
* still sees the full catalogue.
|
|
6147
6186
|
*/
|
|
6148
6187
|
applyOrderlists?: boolean;
|
|
6188
|
+
/**
|
|
6189
|
+
* Attribute names to request per product, e.g. `['MPN']` — makes
|
|
6190
|
+
* `product.attributes` usable in card slots like `belowName`. Unset returns
|
|
6191
|
+
* the first page of ALL attributes (12 per product), so products with more
|
|
6192
|
+
* than 12 silently lose the rest.
|
|
6193
|
+
*/
|
|
6194
|
+
productTrackAttributes?: string[];
|
|
6149
6195
|
/** Number of columns in the grid. Accepts 2, 3, 4, 5, or 6. Defaults to 3. */
|
|
6150
6196
|
columns?: number;
|
|
6151
6197
|
/**
|
|
@@ -6182,6 +6228,11 @@ interface ProductGridProps {
|
|
|
6182
6228
|
/** * 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[];
|
|
6183
6229
|
/** * Active price range lower bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMin?: number;
|
|
6184
6230
|
/** * Active price range upper bound from the FiltersSidebar `onPriceChange`. * Triggers a re-fetch when changed. */ priceFilterMax?: number;
|
|
6231
|
+
/**
|
|
6232
|
+
* Active stock buckets from the filters sidebar. Triggers a re-fetch when
|
|
6233
|
+
* changed. Empty or both buckets means no stock filter.
|
|
6234
|
+
*/
|
|
6235
|
+
availability?: Availability[];
|
|
6185
6236
|
/** * Called when sort state changes internally (for syncing a sibling toolbar). */ onSortChange?: (field: string, order: string) => void;
|
|
6186
6237
|
/** * 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;
|
|
6187
6238
|
/** * 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;
|
|
@@ -7300,6 +7351,8 @@ interface SearchBarProps {
|
|
|
7300
7351
|
* still sees the full catalogue.
|
|
7301
7352
|
*/
|
|
7302
7353
|
applyOrderlists?: boolean;
|
|
7354
|
+
/** Attribute names to request per product, e.g. `['MPN']`. See ProductGrid. */
|
|
7355
|
+
productTrackAttributes?: string[];
|
|
7303
7356
|
/**
|
|
7304
7357
|
* Bump this counter to clear the search input from outside (e.g. on route
|
|
7305
7358
|
* change). Each unique value triggers a one-time reset of the local term.
|
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';
|
|
@@ -2070,7 +2070,7 @@ function useProductInfo(options) {
|
|
|
2070
2070
|
if (options.user && "customerId" in options.user) input.customerId = options.user.customerId;
|
|
2071
2071
|
return input;
|
|
2072
2072
|
}
|
|
2073
|
-
function
|
|
2073
|
+
function buildAttributeInput2() {
|
|
2074
2074
|
const names = options.productTrackAttributes;
|
|
2075
2075
|
if (!names || names.length === 0) return void 0;
|
|
2076
2076
|
return { attributeDescription: { names } };
|
|
@@ -2093,7 +2093,7 @@ function useProductInfo(options) {
|
|
|
2093
2093
|
orderlistIds = (orderlists?.items ?? []).map((ol) => ol.id);
|
|
2094
2094
|
}
|
|
2095
2095
|
const service = createServices(graphqlClient).product;
|
|
2096
|
-
const attributeInput =
|
|
2096
|
+
const attributeInput = buildAttributeInput2();
|
|
2097
2097
|
const variables = {
|
|
2098
2098
|
productId,
|
|
2099
2099
|
language,
|
|
@@ -2183,6 +2183,10 @@ function useProductInfo(options) {
|
|
|
2183
2183
|
};
|
|
2184
2184
|
}
|
|
2185
2185
|
var inflightFetches2 = /* @__PURE__ */ new Set();
|
|
2186
|
+
function buildAttributeInput(names) {
|
|
2187
|
+
if (!names || names.length === 0) return void 0;
|
|
2188
|
+
return { attributeDescription: { names } };
|
|
2189
|
+
}
|
|
2186
2190
|
function useProductSearch(options) {
|
|
2187
2191
|
const { graphqlClient, configuration } = options;
|
|
2188
2192
|
const isControlled = options.products !== void 0;
|
|
@@ -2237,7 +2241,8 @@ function useProductSearch(options) {
|
|
|
2237
2241
|
userKey,
|
|
2238
2242
|
JSON.stringify(options.textFilters ?? []),
|
|
2239
2243
|
options.priceFilterMin ?? "",
|
|
2240
|
-
options.priceFilterMax ?? ""
|
|
2244
|
+
options.priceFilterMax ?? "",
|
|
2245
|
+
JSON.stringify(options.availability ?? [])
|
|
2241
2246
|
].join("|");
|
|
2242
2247
|
if (inflightFetches2.has(inflightKey)) {
|
|
2243
2248
|
fetchIdRef.current--;
|
|
@@ -2279,6 +2284,7 @@ function useProductSearch(options) {
|
|
|
2279
2284
|
}
|
|
2280
2285
|
] : [];
|
|
2281
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);
|
|
2282
2288
|
const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2283
2289
|
const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
|
|
2284
2290
|
const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
@@ -2301,6 +2307,7 @@ function useProductSearch(options) {
|
|
|
2301
2307
|
...options.brand && { manufacturers: [options.brand] },
|
|
2302
2308
|
...options.textFilters?.length && { textFilters: options.textFilters },
|
|
2303
2309
|
...priceFilter && { price: priceFilter },
|
|
2310
|
+
...inventoryFilter && { inventory: inventoryFilter },
|
|
2304
2311
|
...sortInputs.length && { sortInputs },
|
|
2305
2312
|
...options.companyId && { companyId: options.companyId },
|
|
2306
2313
|
...userId !== void 0 && { userId },
|
|
@@ -2315,6 +2322,7 @@ function useProductSearch(options) {
|
|
|
2315
2322
|
const filterAvailableAttributeInput = {
|
|
2316
2323
|
isSearchable: true
|
|
2317
2324
|
};
|
|
2325
|
+
const attributeInput = buildAttributeInput(options.productTrackAttributes);
|
|
2318
2326
|
const variables = {
|
|
2319
2327
|
categoryId: catId,
|
|
2320
2328
|
language: lang,
|
|
@@ -2322,7 +2330,8 @@ function useProductSearch(options) {
|
|
|
2322
2330
|
priceCalculateProductInput,
|
|
2323
2331
|
filterAvailableAttributeInput,
|
|
2324
2332
|
imageSearchFilters: configuration?.imageSearchFiltersGrid,
|
|
2325
|
-
imageVariantFilters: configuration?.imageVariantFiltersMedium
|
|
2333
|
+
imageVariantFilters: configuration?.imageVariantFiltersMedium,
|
|
2334
|
+
...attributeInput && { attributeResultSearchInput: attributeInput }
|
|
2326
2335
|
};
|
|
2327
2336
|
const response = await service.getCategory(variables);
|
|
2328
2337
|
if (thisId !== fetchIdRef.current) return;
|
|
@@ -2375,6 +2384,7 @@ function useProductSearch(options) {
|
|
|
2375
2384
|
options.textFilters,
|
|
2376
2385
|
options.priceFilterMin,
|
|
2377
2386
|
options.priceFilterMax,
|
|
2387
|
+
options.availability,
|
|
2378
2388
|
options.sortField,
|
|
2379
2389
|
options.sortOrder,
|
|
2380
2390
|
options.companyId,
|
|
@@ -2383,7 +2393,9 @@ function useProductSearch(options) {
|
|
|
2383
2393
|
taxZone,
|
|
2384
2394
|
pageSize,
|
|
2385
2395
|
pagination.currentPage,
|
|
2386
|
-
configuration
|
|
2396
|
+
configuration,
|
|
2397
|
+
// Stringified: callers pass an array literal, so identity changes each render.
|
|
2398
|
+
JSON.stringify(options.productTrackAttributes ?? [])
|
|
2387
2399
|
]);
|
|
2388
2400
|
const search = useCallback(
|
|
2389
2401
|
(term) => {
|
|
@@ -2412,6 +2424,7 @@ function useProductSearch(options) {
|
|
|
2412
2424
|
const userId = options.user && "contactId" in options.user ? options.user.contactId : options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2413
2425
|
const contactId = options.user && "contactId" in options.user ? options.user.contactId : void 0;
|
|
2414
2426
|
const customerId = options.user && "customerId" in options.user ? options.user.customerId : void 0;
|
|
2427
|
+
const inventoryFilter = buildInventoryFilter(options.availability);
|
|
2415
2428
|
const categoryProductSearchInput = {
|
|
2416
2429
|
language,
|
|
2417
2430
|
page: 1,
|
|
@@ -2423,6 +2436,7 @@ function useProductSearch(options) {
|
|
|
2423
2436
|
ProductStatus.S
|
|
2424
2437
|
],
|
|
2425
2438
|
hidden: false,
|
|
2439
|
+
...inventoryFilter && { inventory: inventoryFilter },
|
|
2426
2440
|
term,
|
|
2427
2441
|
searchFields: [
|
|
2428
2442
|
{
|
|
@@ -2462,13 +2476,15 @@ function useProductSearch(options) {
|
|
|
2462
2476
|
...contactId !== void 0 && { contactId },
|
|
2463
2477
|
...customerId !== void 0 && { customerId }
|
|
2464
2478
|
};
|
|
2479
|
+
const attributeInput = buildAttributeInput(options.productTrackAttributes);
|
|
2465
2480
|
const variables = {
|
|
2466
2481
|
categoryId: catId,
|
|
2467
2482
|
language,
|
|
2468
2483
|
categoryProductSearchInput,
|
|
2469
2484
|
priceCalculateProductInput,
|
|
2470
2485
|
imageSearchFilters: configuration?.imageSearchFiltersGrid,
|
|
2471
|
-
imageVariantFilters: configuration?.imageVariantFiltersMedium
|
|
2486
|
+
imageVariantFilters: configuration?.imageVariantFiltersMedium,
|
|
2487
|
+
...attributeInput && { attributeResultSearchInput: attributeInput }
|
|
2472
2488
|
};
|
|
2473
2489
|
const response = await service.getCategory(variables);
|
|
2474
2490
|
const productsResponse = response?.products;
|
|
@@ -2487,7 +2503,7 @@ function useProductSearch(options) {
|
|
|
2487
2503
|
}, 300);
|
|
2488
2504
|
},
|
|
2489
2505
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2490
|
-
[graphqlClient, language, configuration, userKey, JSON.stringify(options.orderlistIds ?? []), options.applyOrderlists]
|
|
2506
|
+
[graphqlClient, language, configuration, userKey, JSON.stringify(options.orderlistIds ?? []), options.applyOrderlists, JSON.stringify(options.productTrackAttributes ?? [])]
|
|
2491
2507
|
);
|
|
2492
2508
|
useEffect(() => {
|
|
2493
2509
|
if (!isControlled) fetchProducts();
|
|
@@ -2500,6 +2516,7 @@ function useProductSearch(options) {
|
|
|
2500
2516
|
JSON.stringify(options.textFilters),
|
|
2501
2517
|
options.priceFilterMin,
|
|
2502
2518
|
options.priceFilterMax,
|
|
2519
|
+
options.availability,
|
|
2503
2520
|
options.sortField,
|
|
2504
2521
|
options.sortOrder,
|
|
2505
2522
|
pageSize,
|
|
@@ -9014,6 +9031,7 @@ function ProductGrid(rawProps) {
|
|
|
9014
9031
|
brand: props.brand,
|
|
9015
9032
|
orderlistIds: props.orderlistIds,
|
|
9016
9033
|
applyOrderlists: props.applyOrderlists,
|
|
9034
|
+
productTrackAttributes: props.productTrackAttributes,
|
|
9017
9035
|
language: props.language,
|
|
9018
9036
|
taxZone: props.taxZone,
|
|
9019
9037
|
user: props.user,
|
|
@@ -9021,6 +9039,7 @@ function ProductGrid(rawProps) {
|
|
|
9021
9039
|
textFilters: props.textFilters,
|
|
9022
9040
|
priceFilterMin: props.priceFilterMin,
|
|
9023
9041
|
priceFilterMax: props.priceFilterMax,
|
|
9042
|
+
availability: props.availability,
|
|
9024
9043
|
sortField: props.sortField,
|
|
9025
9044
|
sortOrder: props.sortOrder,
|
|
9026
9045
|
pageSize: props.pageSize,
|
|
@@ -9323,11 +9342,18 @@ function GridFilters(rawProps) {
|
|
|
9323
9342
|
const [isPending, setIsPending] = useState(() => false);
|
|
9324
9343
|
const [appliedMin, setAppliedMin] = useState(() => void 0);
|
|
9325
9344
|
const [appliedMax, setAppliedMax] = useState(() => void 0);
|
|
9345
|
+
const [availability, setAvailability] = useState(
|
|
9346
|
+
() => [...props.activeAvailability || []]
|
|
9347
|
+
);
|
|
9326
9348
|
function showPriceFilter() {
|
|
9327
9349
|
const mode = props.portalMode || "open";
|
|
9328
9350
|
if (mode === "open") return true;
|
|
9329
9351
|
return !!props.user;
|
|
9330
9352
|
}
|
|
9353
|
+
function showAvailability() {
|
|
9354
|
+
if (!props.showAvailabilityFilter) return false;
|
|
9355
|
+
return !isContentHidden(props.portalMode, props.user);
|
|
9356
|
+
}
|
|
9331
9357
|
function getFilterName(filter) {
|
|
9332
9358
|
return filter?.attributeDescription?.name || "";
|
|
9333
9359
|
}
|
|
@@ -9372,6 +9398,13 @@ function GridFilters(rawProps) {
|
|
|
9372
9398
|
props.onFilterChange(filter, value);
|
|
9373
9399
|
if (props.getSelectedFilters) props.getSelectedFilters();
|
|
9374
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
|
+
}
|
|
9375
9408
|
function handleMinChange(value) {
|
|
9376
9409
|
const n = value > currentMax ? currentMax : value;
|
|
9377
9410
|
setCurrentMin(n);
|
|
@@ -9445,11 +9478,16 @@ function GridFilters(rawProps) {
|
|
|
9445
9478
|
setCurrentMin(props.priceMin || 0);
|
|
9446
9479
|
setCurrentMax(props.priceMax || 9999);
|
|
9447
9480
|
setExpandedFilters({});
|
|
9481
|
+
setAvailability([]);
|
|
9448
9482
|
}, [props.clearSignal, props.priceMin, props.priceMax]);
|
|
9449
9483
|
useEffect(() => {
|
|
9450
9484
|
if (!props.activeTextFilters) return;
|
|
9451
9485
|
setSelectedFilters(props.activeTextFilters);
|
|
9452
9486
|
}, [props.activeTextFilters]);
|
|
9487
|
+
useEffect(() => {
|
|
9488
|
+
if (!props.activeAvailability) return;
|
|
9489
|
+
setAvailability([...props.activeAvailability]);
|
|
9490
|
+
}, [props.activeAvailability]);
|
|
9453
9491
|
useEffect(() => {
|
|
9454
9492
|
if (props.activePriceMin === void 0 && props.activePriceMax === void 0) {
|
|
9455
9493
|
setCurrentMin(props.priceMin || 0);
|
|
@@ -9549,6 +9587,40 @@ function GridFilters(rawProps) {
|
|
|
9549
9587
|
" ",
|
|
9550
9588
|
/* @__PURE__ */ jsx("div", { className: "propeller-grid-filters__divider h-px bg-border-subtle" })
|
|
9551
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,
|
|
9552
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,
|
|
9553
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: [
|
|
9554
9626
|
/* @__PURE__ */ jsxs(
|
|
@@ -9724,6 +9796,8 @@ var DEFAULT_LABELS = {
|
|
|
9724
9796
|
results: "results",
|
|
9725
9797
|
perPage: " per page",
|
|
9726
9798
|
price: "Price",
|
|
9799
|
+
inStock: "In stock",
|
|
9800
|
+
outOfStock: "Out of stock",
|
|
9727
9801
|
switchToList: "Switch to list view",
|
|
9728
9802
|
switchToGrid: "Switch to grid view"
|
|
9729
9803
|
};
|
|
@@ -9752,7 +9826,8 @@ function GridToolbar(rawProps) {
|
|
|
9752
9826
|
const text = props.activeTextFilters || {};
|
|
9753
9827
|
const hasText = Object.keys(text).some((k) => (text[k] || []).length > 0);
|
|
9754
9828
|
const hasPrice = props.priceFilterMin !== void 0 || props.priceFilterMax !== void 0;
|
|
9755
|
-
|
|
9829
|
+
const hasAvailability = (props.availability || []).length > 0;
|
|
9830
|
+
return hasText || hasPrice || hasAvailability;
|
|
9756
9831
|
}
|
|
9757
9832
|
function getActiveFilterBadges() {
|
|
9758
9833
|
const text = props.activeTextFilters || {};
|
|
@@ -9770,6 +9845,9 @@ function GridToolbar(rawProps) {
|
|
|
9770
9845
|
function isPriceSortDisabled() {
|
|
9771
9846
|
return isContentHidden(props.portalMode, props.user);
|
|
9772
9847
|
}
|
|
9848
|
+
function getAvailabilityLabel(value) {
|
|
9849
|
+
return value === "in-stock" ? getLabel56("inStock") : getLabel56("outOfStock");
|
|
9850
|
+
}
|
|
9773
9851
|
function handleSortFieldChange(field) {
|
|
9774
9852
|
setCurrentSortField(field);
|
|
9775
9853
|
if (props.onSortChange) props.onSortChange(field, currentSortOrder);
|
|
@@ -9943,6 +10021,20 @@ function GridToolbar(rawProps) {
|
|
|
9943
10021
|
]
|
|
9944
10022
|
}
|
|
9945
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
|
+
)),
|
|
9946
10038
|
getActiveFilterBadges()?.map((badge) => /* @__PURE__ */ jsxs(
|
|
9947
10039
|
"span",
|
|
9948
10040
|
{
|
|
@@ -18376,7 +18468,8 @@ function SearchBar(rawProps) {
|
|
|
18376
18468
|
user: props.user,
|
|
18377
18469
|
companyId: props.companyId,
|
|
18378
18470
|
orderlistIds: props.orderlistIds,
|
|
18379
|
-
applyOrderlists: props.applyOrderlists
|
|
18471
|
+
applyOrderlists: props.applyOrderlists,
|
|
18472
|
+
productTrackAttributes: props.productTrackAttributes
|
|
18380
18473
|
});
|
|
18381
18474
|
const results = searchResults.slice(0, maxResults).map((item) => mapToSearchBarResult(item, props.language));
|
|
18382
18475
|
const itemsFound = searchItemsFound;
|