@propeller-commerce/propeller-v2-react-ui 0.22.0 → 0.23.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 +37 -0
- package/dist/index.cjs +84 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -3
- package/dist/index.d.ts +40 -3
- package/dist/index.js +84 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,43 @@ 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.23.0] - 2026-09-24
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **A machine listed in one language could not be opened.** 0.21.0 stopped the
|
|
16
|
+
listing from narrowing to a single language, so an NL-only installation
|
|
17
|
+
appears in an EN tree and is linked by its NL slug — but `machine(slug:)`
|
|
18
|
+
resolves a slug **only** in the language it was authored in, so following
|
|
19
|
+
that link asked for `machine(slug: "<NL slug>", language: "EN")` and got
|
|
20
|
+
`SPARE_PARTS_MACHINE_NOT_FOUND_ERROR`. The page then showed a title derived
|
|
21
|
+
from the slug, no parts and no error: the bug moved one click deeper instead
|
|
22
|
+
of going away. `useSpareParts` now tries the tree language, then the
|
|
23
|
+
storefront language, then any locales given in the new `machineLanguages`
|
|
24
|
+
option, and upper-cases each (the API matches case exactly — `nl` misses
|
|
25
|
+
where `NL` hits). A slug that resolves in none of them sets the new
|
|
26
|
+
`notFound` flag, and `MachineGrid` renders "This machine could not be found."
|
|
27
|
+
instead of an empty listing. A fully-translated shop still costs exactly one
|
|
28
|
+
request, and a non-"not found" failure is never retried or reported as a
|
|
29
|
+
missing machine. (PWP-993)
|
|
30
|
+
|
|
31
|
+
- **"Qty in machine" and "Search parts…" stayed English in a translated shop.**
|
|
32
|
+
Both were read from `toolbarLabels`, which is forwarded verbatim to
|
|
33
|
+
`GridToolbar` — so they belonged to no toolbar dictionary, and a shop that
|
|
34
|
+
translated its `GridToolbar` keys properly still got English. They now come
|
|
35
|
+
from `machineCardLabels` (the grid's own label bag, alongside `loading`,
|
|
36
|
+
`noMachines` and the new `machineNotFound`), falling back to `toolbarLabels`
|
|
37
|
+
so hosts that already set them keep working. (PWP-995a)
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- `MachineGrid.machineLanguages` / `useSpareParts.machineLanguages` — the
|
|
42
|
+
locales to try when a machine slug does not resolve in the tree language.
|
|
43
|
+
- `useSpareParts().notFound` — the slug resolved in none of the candidate
|
|
44
|
+
languages, as distinct from "resolved but has no parts".
|
|
45
|
+
- `machineCardLabels.machineNotFound` — copy for that state.
|
|
46
|
+
|
|
47
|
+
|
|
11
48
|
## [0.22.0] - 2026-09-24
|
|
12
49
|
|
|
13
50
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -2782,6 +2782,40 @@ function useQuickOrder(options) {
|
|
|
2782
2782
|
);
|
|
2783
2783
|
return { submitting, error, searchProducts, submit };
|
|
2784
2784
|
}
|
|
2785
|
+
|
|
2786
|
+
// src/composables/shared/utils/machineLanguage.ts
|
|
2787
|
+
var MACHINE_NOT_FOUND = "SPARE_PARTS_MACHINE_NOT_FOUND_ERROR";
|
|
2788
|
+
function isMachineNotFound(error) {
|
|
2789
|
+
const entries = error?.errors;
|
|
2790
|
+
if (!Array.isArray(entries)) return false;
|
|
2791
|
+
return entries.some(
|
|
2792
|
+
(e) => e?.extensions?.code === MACHINE_NOT_FOUND || /no machine found for slug and language/i.test(e?.message ?? "")
|
|
2793
|
+
);
|
|
2794
|
+
}
|
|
2795
|
+
function machineLanguageCandidates(machineLanguage, language, extra) {
|
|
2796
|
+
const ordered = [machineLanguage, language, ...extra ?? []];
|
|
2797
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2798
|
+
const out = [];
|
|
2799
|
+
for (const raw of ordered) {
|
|
2800
|
+
const value = raw?.trim().toUpperCase();
|
|
2801
|
+
if (!value || seen.has(value)) continue;
|
|
2802
|
+
seen.add(value);
|
|
2803
|
+
out.push(value);
|
|
2804
|
+
}
|
|
2805
|
+
return out;
|
|
2806
|
+
}
|
|
2807
|
+
async function resolveMachineAcrossLanguages(candidates, fetchOne) {
|
|
2808
|
+
for (const language of candidates) {
|
|
2809
|
+
try {
|
|
2810
|
+
return { machine: await fetchOne(language), language };
|
|
2811
|
+
} catch (error) {
|
|
2812
|
+
if (!isMachineNotFound(error)) throw error;
|
|
2813
|
+
}
|
|
2814
|
+
}
|
|
2815
|
+
return null;
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2818
|
+
// src/composables/react/useSpareParts.ts
|
|
2785
2819
|
var inflightFetches3 = /* @__PURE__ */ new Set();
|
|
2786
2820
|
var STOREFRONT_STATUSES = [
|
|
2787
2821
|
propellerSdkV2.ProductStatus.A,
|
|
@@ -2798,6 +2832,8 @@ function useSpareParts(options) {
|
|
|
2798
2832
|
const [itemsFound, setItemsFound] = React6.useState(0);
|
|
2799
2833
|
const [internalLoading, setInternalLoading] = React6.useState(false);
|
|
2800
2834
|
const [internalPage, setInternalPage] = React6.useState(1);
|
|
2835
|
+
const [notFound, setNotFound] = React6.useState(false);
|
|
2836
|
+
const machineLanguagesKey = (options.machineLanguages ?? []).join(",");
|
|
2801
2837
|
const currentPage = options.page ?? internalPage;
|
|
2802
2838
|
const [totalPages, setTotalPages] = React6.useState(1);
|
|
2803
2839
|
const fetchIdRef = React6.useRef(0);
|
|
@@ -2863,17 +2899,35 @@ function useSpareParts(options) {
|
|
|
2863
2899
|
const filterAvailableAttributeInput = {
|
|
2864
2900
|
isSearchable: true
|
|
2865
2901
|
};
|
|
2866
|
-
const
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2902
|
+
const resolved = await resolveMachineAcrossLanguages(
|
|
2903
|
+
machineLanguageCandidates(
|
|
2904
|
+
options.machineLanguage,
|
|
2905
|
+
options.language,
|
|
2906
|
+
options.machineLanguages
|
|
2907
|
+
),
|
|
2908
|
+
(language) => service.getMachine({
|
|
2909
|
+
slug: options.slug,
|
|
2910
|
+
// The machine tree's language, NOT the parts' — see `machineLanguage`.
|
|
2911
|
+
language,
|
|
2912
|
+
sparePartsMachineProductSearchInput,
|
|
2913
|
+
filterAvailableAttributeInput,
|
|
2914
|
+
priceCalculateProductInput,
|
|
2915
|
+
imageSearchFilters: options.configuration?.imageSearchFiltersGrid,
|
|
2916
|
+
imageVariantFilters: options.configuration?.imageVariantFiltersMedium
|
|
2917
|
+
})
|
|
2918
|
+
);
|
|
2876
2919
|
if (thisId !== fetchIdRef.current) return;
|
|
2920
|
+
if (!resolved) {
|
|
2921
|
+
setNotFound(true);
|
|
2922
|
+
setInternalParts([]);
|
|
2923
|
+
setChildMachines([]);
|
|
2924
|
+
setItemsFound(0);
|
|
2925
|
+
options.onItemsFoundChange?.(0);
|
|
2926
|
+
setTotalPages(1);
|
|
2927
|
+
return;
|
|
2928
|
+
}
|
|
2929
|
+
setNotFound(false);
|
|
2930
|
+
const machine = resolved.machine;
|
|
2877
2931
|
const partsResponse = machine?.sparePartProducts;
|
|
2878
2932
|
const items = partsResponse?.items ?? [];
|
|
2879
2933
|
setInternalParts(items);
|
|
@@ -2915,6 +2969,7 @@ function useSpareParts(options) {
|
|
|
2915
2969
|
options.term,
|
|
2916
2970
|
options.language,
|
|
2917
2971
|
options.machineLanguage,
|
|
2972
|
+
machineLanguagesKey,
|
|
2918
2973
|
options.taxZone,
|
|
2919
2974
|
options.companyId,
|
|
2920
2975
|
options.textFilters,
|
|
@@ -2935,6 +2990,7 @@ function useSpareParts(options) {
|
|
|
2935
2990
|
options.term,
|
|
2936
2991
|
options.language,
|
|
2937
2992
|
options.machineLanguage,
|
|
2993
|
+
machineLanguagesKey,
|
|
2938
2994
|
options.companyId,
|
|
2939
2995
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2940
2996
|
JSON.stringify(options.textFilters ?? []),
|
|
@@ -2960,7 +3016,8 @@ function useSpareParts(options) {
|
|
|
2960
3016
|
currentPage,
|
|
2961
3017
|
totalPages,
|
|
2962
3018
|
fetchParts,
|
|
2963
|
-
goToPage
|
|
3019
|
+
goToPage,
|
|
3020
|
+
notFound: !isControlled && notFound
|
|
2964
3021
|
};
|
|
2965
3022
|
}
|
|
2966
3023
|
var inflightFetches4 = /* @__PURE__ */ new Set();
|
|
@@ -10504,13 +10561,14 @@ function MachineGrid(rawProps) {
|
|
|
10504
10561
|
// node query threads through useSpareParts.
|
|
10505
10562
|
imageVariantFilters: configuration?.imageVariantFiltersMedium
|
|
10506
10563
|
});
|
|
10507
|
-
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages } = useSpareParts({
|
|
10564
|
+
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages, notFound } = useSpareParts({
|
|
10508
10565
|
graphqlClient: props.graphqlClient,
|
|
10509
10566
|
// Idle at the root (no slug).
|
|
10510
10567
|
slug: isRoot ? void 0 : currentSlug,
|
|
10511
10568
|
term: term || void 0,
|
|
10512
10569
|
language,
|
|
10513
10570
|
machineLanguage,
|
|
10571
|
+
machineLanguages: rawProps.machineLanguages,
|
|
10514
10572
|
taxZone: props.taxZone,
|
|
10515
10573
|
user: props.user,
|
|
10516
10574
|
companyId: props.companyId,
|
|
@@ -10534,6 +10592,7 @@ function MachineGrid(rawProps) {
|
|
|
10534
10592
|
onMachineChange: setMachine
|
|
10535
10593
|
});
|
|
10536
10594
|
const machineName = machine ? propellerV2CoreUi.getLocalizedValue(machine.name, machineLanguage) : slugToLabel(currentSlug);
|
|
10595
|
+
const machineLabel = (key, fallback) => rawProps.machineCardLabels?.[key] ?? rawProps.toolbarLabels?.[key] ?? fallback;
|
|
10537
10596
|
const partProducts = React6.useMemo(
|
|
10538
10597
|
() => displayParts.map((p) => p.product).filter(Boolean),
|
|
10539
10598
|
[displayParts]
|
|
@@ -10608,6 +10667,12 @@ function MachineGrid(rawProps) {
|
|
|
10608
10667
|
] });
|
|
10609
10668
|
}
|
|
10610
10669
|
const hasParts = itemsFound > 0 || partProducts.length > 0;
|
|
10670
|
+
if (notFound && !partsLoading) {
|
|
10671
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: rawProps.className, children: [
|
|
10672
|
+
/* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Breadcrumb", className: "propeller-breadcrumbs mb-6", children: /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "flex flex-wrap items-center gap-2 text-sm text-foreground-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: basePath, className: "hover:text-primary", children: rawProps.rootTitle ?? "Machines" }) }) }) }),
|
|
10673
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-12 text-center text-foreground-subtle", children: machineLabel("machineNotFound", "This machine could not be found.") })
|
|
10674
|
+
] });
|
|
10675
|
+
}
|
|
10611
10676
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: rawProps.className, children: [
|
|
10612
10677
|
/* @__PURE__ */ jsxRuntime.jsx("nav", { "aria-label": "Breadcrumb", className: "propeller-breadcrumbs mb-6", children: /* @__PURE__ */ jsxRuntime.jsxs("ol", { className: "flex flex-wrap items-center gap-2 text-sm text-foreground-subtle", children: [
|
|
10613
10678
|
/* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: basePath, className: "hover:text-primary", children: rawProps.rootTitle ?? "Machines" }) }),
|
|
@@ -10660,7 +10725,9 @@ function MachineGrid(rawProps) {
|
|
|
10660
10725
|
{
|
|
10661
10726
|
defaultValue: term,
|
|
10662
10727
|
onSubmit: submitSearch,
|
|
10663
|
-
|
|
10728
|
+
searchLabel: machineLabel("searchParts", "Search parts\u2026"),
|
|
10729
|
+
searchAriaLabel: machineLabel("searchParts", "Search parts"),
|
|
10730
|
+
buttonLabel: propellerV2CoreUi.getLabel(rawProps.toolbarLabels, "search", "Search")
|
|
10664
10731
|
},
|
|
10665
10732
|
term
|
|
10666
10733
|
),
|
|
@@ -10711,7 +10778,7 @@ function MachineGrid(rawProps) {
|
|
|
10711
10778
|
const qty = quantityBySku.get(product.sku);
|
|
10712
10779
|
if (!qty) return null;
|
|
10713
10780
|
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "propeller-spare-part__quantity text-sm text-foreground-subtle", children: [
|
|
10714
|
-
|
|
10781
|
+
machineLabel("quantityInMachine", "Qty in machine"),
|
|
10715
10782
|
": ",
|
|
10716
10783
|
qty
|
|
10717
10784
|
] });
|
|
@@ -10743,8 +10810,8 @@ function MachinePartsSearch(props) {
|
|
|
10743
10810
|
onKeyDown: (e) => {
|
|
10744
10811
|
if (e.key === "Enter") submit();
|
|
10745
10812
|
},
|
|
10746
|
-
placeholder: props.
|
|
10747
|
-
"aria-label": props.
|
|
10813
|
+
placeholder: props.searchLabel,
|
|
10814
|
+
"aria-label": props.searchAriaLabel,
|
|
10748
10815
|
className: "w-full rounded border border-border bg-background px-3 py-2"
|
|
10749
10816
|
}
|
|
10750
10817
|
),
|
|
@@ -10754,7 +10821,7 @@ function MachinePartsSearch(props) {
|
|
|
10754
10821
|
type: "button",
|
|
10755
10822
|
onClick: submit,
|
|
10756
10823
|
className: "rounded bg-primary px-4 py-2 text-primary-foreground",
|
|
10757
|
-
children: props.
|
|
10824
|
+
children: props.buttonLabel
|
|
10758
10825
|
}
|
|
10759
10826
|
)
|
|
10760
10827
|
] });
|