@propeller-commerce/propeller-v2-react-ui 0.15.7 → 0.15.9

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 CHANGED
@@ -8,6 +8,55 @@ once it reaches 1.0. Until then (the `0.x` line) the public API may change
8
8
  between minor versions; breaking changes are called out below and in
9
9
  [MIGRATION.md](./MIGRATION.md).
10
10
 
11
+ ## [0.15.9] - 2026-08-11
12
+
13
+ ### Fixed
14
+
15
+ - **The product specifications table emptied out in a language the catalogue is
16
+ only partly translated into** (PWP-928). A localized list carries only the
17
+ languages someone authored, so asking for one nobody wrote returns nothing —
18
+ it does not fall back. `ProductSpecifications` matched strictly on
19
+ `language === props.language`, which failed twice over: the label fell through
20
+ to `attributeDescription.name` and printed the raw PIM code
21
+ (`QUANTORE_70001706`) at the user, and the value resolved to `''` — and since
22
+ `getAttributes()` drops rows with an empty value, the row disappeared
23
+ entirely. On a Dutch-only catalogue browsed in English the whole table went
24
+ blank. Both now prefer the active language and fall back to whichever
25
+ translation exists, matching `GridFilters`, `ProductDescription` and the menu.
26
+ Empty localized rows the backend really does return
27
+ (`{ language: 'FR', values: [] }`) are skipped rather than winning the
28
+ fallback. Language matching is now case-insensitive: with `language="en"` the
29
+ component previously rendered nothing at all.
30
+
31
+ ## [0.15.8] - 2026-08-11
32
+
33
+ ### Fixed
34
+
35
+ - **Quick order could add products from outside the user's catalogue.** Both the
36
+ row typeahead and the XLSX upload resolved codes through the flat `products`
37
+ resolver with no catalog scope and no user context. Orderlist (contract)
38
+ scoping is honoured by `category.products` but **silently ignored** on the flat
39
+ resolver, so quick order surfaced the full catalogue while the grid and the
40
+ search preview stayed scoped — the same bug fixed for the SearchBar preview in
41
+ 0.4.28. `searchProducts` now queries `category.getCategory` over
42
+ `configuration.baseCategoryId` and passes `userId` / `companyId` /
43
+ `applyOrderlists`, plus `hidden: false`, matching `ProductGrid`, the SearchBar
44
+ and the WordPress plugin's quick-order flow. Codes outside the catalogue are
45
+ reported through `onMissingCodes` instead of being added.
46
+
47
+ ### Changed
48
+
49
+ - **`QuickOrder` needs `configuration.baseCategoryId`.** Without it the
50
+ typeahead and the upload resolve nothing rather than falling back to an
51
+ unscoped search — failing closed is the point of the fix. Apps already
52
+ resolving a base category for `ProductGrid` / `SearchBar` should pass the same
53
+ value.
54
+
55
+ ### Added
56
+
57
+ - `QuickOrder` accepts `taxZone` (defaults to `'NL'`), `orderlistIds` and
58
+ `applyOrderlists`, forwarded to the scoped search.
59
+
11
60
  ## [0.15.7] - 2026-08-10
12
61
 
13
62
  ### Fixed
package/dist/index.cjs CHANGED
@@ -2596,21 +2596,33 @@ function useQuickOrder(options) {
2596
2596
  } = options;
2597
2597
  const language = options.language || "NL";
2598
2598
  const searchLimit = options.searchLimit ?? 8;
2599
+ const taxZone = options.taxZone || "NL";
2600
+ const { orderlistIds, applyOrderlists } = options;
2599
2601
  const [submitting, setSubmitting] = React6.useState(false);
2600
2602
  const [error, setError] = React6.useState(null);
2601
2603
  const searchProducts = React6.useCallback(
2602
2604
  async (term) => {
2603
2605
  const trimmed = term.trim();
2604
2606
  if (!trimmed || !graphqlClient) return [];
2607
+ const catId = configuration.baseCategoryId ?? 0;
2608
+ if (!catId) return [];
2605
2609
  try {
2606
- const service = propellerV2CoreUi.createServices(graphqlClient).product;
2610
+ const service = propellerV2CoreUi.createServices(graphqlClient).category;
2611
+ const orderlistScope = orderlistIds && orderlistIds.length > 0 ? { applyOrderlists: applyOrderlists !== false, orderlistIds } : { applyOrderlists: false };
2612
+ const userId = user && "contactId" in user ? user.contactId : user && "customerId" in user ? user.customerId : void 0;
2613
+ const contactId = user && "contactId" in user ? user.contactId : void 0;
2614
+ const customerId = user && "customerId" in user ? user.customerId : void 0;
2607
2615
  const input = {
2608
2616
  term: trimmed,
2609
2617
  language,
2610
2618
  page: 1,
2611
2619
  offset: searchLimit,
2612
2620
  statuses: [propellerSdkV2.ProductStatus.A, propellerSdkV2.ProductStatus.P, propellerSdkV2.ProductStatus.T, propellerSdkV2.ProductStatus.S],
2621
+ hidden: false,
2613
2622
  sortInputs: [{ field: propellerSdkV2.ProductSortField.RELEVANCE, order: propellerSdkV2.SortOrder.DESC }],
2623
+ ...companyId && { companyId },
2624
+ ...userId !== void 0 && { userId },
2625
+ ...orderlistScope,
2614
2626
  searchFields: [
2615
2627
  {
2616
2628
  fieldNames: [
@@ -2634,18 +2646,36 @@ function useQuickOrder(options) {
2634
2646
  ]
2635
2647
  };
2636
2648
  const variables = {
2637
- input,
2649
+ categoryId: catId,
2638
2650
  language,
2651
+ categoryProductSearchInput: input,
2652
+ priceCalculateProductInput: {
2653
+ taxZone,
2654
+ ...companyId && { companyId },
2655
+ ...contactId !== void 0 && { contactId },
2656
+ ...customerId !== void 0 && { customerId }
2657
+ },
2658
+ imageSearchFilters: configuration.imageSearchFiltersGrid,
2639
2659
  imageVariantFilters: configuration.imageVariantFiltersSmall
2640
2660
  };
2641
- const result = await service.getProducts(variables);
2642
- const items = result?.items ?? [];
2661
+ const response = await service.getCategory(variables);
2662
+ const items = response?.products?.items ?? [];
2643
2663
  return items.map((it) => toMatch(it, language));
2644
2664
  } catch {
2645
2665
  return [];
2646
2666
  }
2647
2667
  },
2648
- [graphqlClient, language, searchLimit, configuration]
2668
+ [
2669
+ graphqlClient,
2670
+ language,
2671
+ searchLimit,
2672
+ configuration,
2673
+ user,
2674
+ companyId,
2675
+ taxZone,
2676
+ orderlistIds,
2677
+ applyOrderlists
2678
+ ]
2649
2679
  );
2650
2680
  const submit = React6.useCallback(
2651
2681
  async (lines) => {
@@ -14160,6 +14190,9 @@ function QuickOrderInner(props) {
14160
14190
  companyId,
14161
14191
  language,
14162
14192
  configuration,
14193
+ taxZone: props.taxZone,
14194
+ orderlistIds: props.orderlistIds,
14195
+ applyOrderlists: props.applyOrderlists,
14163
14196
  onCartCreated,
14164
14197
  afterAddToCart
14165
14198
  });
@@ -16456,19 +16489,27 @@ function ProductSpecifications(rawProps) {
16456
16489
  (a) => (a.attributeDescription?.group || "") === group
16457
16490
  );
16458
16491
  }
16492
+ function pickLocalized(rows, isUsable) {
16493
+ const list = (rows || []).filter(isUsable);
16494
+ const lang = (props.language || "NL").toUpperCase();
16495
+ return list.find((row) => (row.language || "").toUpperCase() === lang) || list[0];
16496
+ }
16459
16497
  function getAttributeLabel(attr) {
16460
- const lang = props.language || "NL";
16461
- const descs = attr.attributeDescription?.descriptions || [];
16462
- const match = descs.find((d) => d.language === lang);
16498
+ const match = pickLocalized(
16499
+ attr.attributeDescription?.descriptions,
16500
+ (d) => !!d.value
16501
+ );
16463
16502
  return match?.value || attr.attributeDescription?.name || "";
16464
16503
  }
16465
16504
  function getAttributeValue(attr) {
16466
16505
  const v = attr.value;
16467
16506
  if (!v) return "";
16468
- const lang = props.language || "NL";
16469
16507
  if (v.type === propellerSdkV2.AttributeType.TEXT) {
16470
16508
  const tv = v;
16471
- const entry = tv.textValues?.find((row) => row.language === lang);
16509
+ const entry = pickLocalized(
16510
+ tv.textValues,
16511
+ (row) => (row?.values || []).some(Boolean)
16512
+ );
16472
16513
  const vals = (entry?.values || []).filter(Boolean);
16473
16514
  return vals.join(", ");
16474
16515
  }