@propeller-commerce/propeller-v2-react-ui 0.22.0 → 0.23.1
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 +54 -0
- package/dist/index.cjs +85 -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 +85 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1681,6 +1681,15 @@ interface UseSparePartsOptions {
|
|
|
1681
1681
|
* parts list need different languages. Set this when they diverge.
|
|
1682
1682
|
*/
|
|
1683
1683
|
machineLanguage?: string;
|
|
1684
|
+
/**
|
|
1685
|
+
* Extra languages to try when the slug does not resolve in `machineLanguage`.
|
|
1686
|
+
*
|
|
1687
|
+
* A slug resolves only in the language it was authored in, so a tree that is
|
|
1688
|
+
* only half-translated has machines reachable by an NL slug but not an EN
|
|
1689
|
+
* one. `machineLanguage` and `language` are always tried first; list the
|
|
1690
|
+
* shop's other locales here to cover the rest. Order is the try order.
|
|
1691
|
+
*/
|
|
1692
|
+
machineLanguages?: string[];
|
|
1684
1693
|
/** Tax zone for price calculation. */
|
|
1685
1694
|
taxZone?: string;
|
|
1686
1695
|
/** Active user — drives `userId` scoping and contact/customer pricing. */
|
|
@@ -1749,6 +1758,14 @@ interface UseSparePartsReturn {
|
|
|
1749
1758
|
fetchParts: () => Promise<void>;
|
|
1750
1759
|
/** Navigate to a page. */
|
|
1751
1760
|
goToPage: (page: number) => void;
|
|
1761
|
+
/**
|
|
1762
|
+
* The slug resolved in none of the candidate languages.
|
|
1763
|
+
*
|
|
1764
|
+
* Distinct from "resolved but has no parts": this node does not exist, and a
|
|
1765
|
+
* host that renders the usual empty listing for it shows a page built
|
|
1766
|
+
* entirely from the URL. Always `false` in controlled mode.
|
|
1767
|
+
*/
|
|
1768
|
+
notFound: boolean;
|
|
1752
1769
|
}
|
|
1753
1770
|
/**
|
|
1754
1771
|
* useSpareParts — fetch and paginate a machine node's spare-parts list.
|
|
@@ -4292,6 +4309,16 @@ interface MachineGridProps {
|
|
|
4292
4309
|
rootTitle?: string;
|
|
4293
4310
|
/** Language the machine tree is authored in (usually EN). Defaults to `'EN'`. */
|
|
4294
4311
|
machineLanguage?: string;
|
|
4312
|
+
/**
|
|
4313
|
+
* Other languages the tree may be authored in, tried in order when a slug
|
|
4314
|
+
* does not resolve in `machineLanguage`.
|
|
4315
|
+
*
|
|
4316
|
+
* A slug resolves only in its own language, so a half-translated tree lists a
|
|
4317
|
+
* machine by its NL slug and then cannot open it with `language: 'EN'`. Pass
|
|
4318
|
+
* the shop's locales here; `machineLanguage` and the storefront `language`
|
|
4319
|
+
* are always tried first (PWP-993).
|
|
4320
|
+
*/
|
|
4321
|
+
machineLanguages?: string[];
|
|
4295
4322
|
listing: MachineListingState;
|
|
4296
4323
|
onListingChange: (next: MachineListingState) => void;
|
|
4297
4324
|
graphqlClient?: GraphQLClient;
|
|
@@ -4331,9 +4358,19 @@ interface MachineGridProps {
|
|
|
4331
4358
|
filtersLabels?: Record<string, string>;
|
|
4332
4359
|
toolbarLabels?: Record<string, string>;
|
|
4333
4360
|
/**
|
|
4334
|
-
* Labels for the machine side of the grid.
|
|
4335
|
-
*
|
|
4336
|
-
*
|
|
4361
|
+
* Labels for the machine side of the grid.
|
|
4362
|
+
*
|
|
4363
|
+
* Keys read here: `loading`, `noMachines`, `machineNotFound`,
|
|
4364
|
+
* `quantityInMachine` and `searchParts`. The same object is passed to each
|
|
4365
|
+
* `MachineCard` as its `labels`, which reads `viewMachine` — so all six keys
|
|
4366
|
+
* belong in it.
|
|
4367
|
+
*
|
|
4368
|
+
* `quantityInMachine` and `searchParts` used to be read from `toolbarLabels`,
|
|
4369
|
+
* which is forwarded verbatim to `GridToolbar`: a shop that translated its
|
|
4370
|
+
* toolbar dictionary properly still got "Qty in machine" and "Search parts…"
|
|
4371
|
+
* in English, because those keys belong to no toolbar (PWP-995a). They are
|
|
4372
|
+
* still read from `toolbarLabels` as a fallback so existing hosts keep
|
|
4373
|
+
* working.
|
|
4337
4374
|
*/
|
|
4338
4375
|
machineCardLabels?: Record<string, string>;
|
|
4339
4376
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1681,6 +1681,15 @@ interface UseSparePartsOptions {
|
|
|
1681
1681
|
* parts list need different languages. Set this when they diverge.
|
|
1682
1682
|
*/
|
|
1683
1683
|
machineLanguage?: string;
|
|
1684
|
+
/**
|
|
1685
|
+
* Extra languages to try when the slug does not resolve in `machineLanguage`.
|
|
1686
|
+
*
|
|
1687
|
+
* A slug resolves only in the language it was authored in, so a tree that is
|
|
1688
|
+
* only half-translated has machines reachable by an NL slug but not an EN
|
|
1689
|
+
* one. `machineLanguage` and `language` are always tried first; list the
|
|
1690
|
+
* shop's other locales here to cover the rest. Order is the try order.
|
|
1691
|
+
*/
|
|
1692
|
+
machineLanguages?: string[];
|
|
1684
1693
|
/** Tax zone for price calculation. */
|
|
1685
1694
|
taxZone?: string;
|
|
1686
1695
|
/** Active user — drives `userId` scoping and contact/customer pricing. */
|
|
@@ -1749,6 +1758,14 @@ interface UseSparePartsReturn {
|
|
|
1749
1758
|
fetchParts: () => Promise<void>;
|
|
1750
1759
|
/** Navigate to a page. */
|
|
1751
1760
|
goToPage: (page: number) => void;
|
|
1761
|
+
/**
|
|
1762
|
+
* The slug resolved in none of the candidate languages.
|
|
1763
|
+
*
|
|
1764
|
+
* Distinct from "resolved but has no parts": this node does not exist, and a
|
|
1765
|
+
* host that renders the usual empty listing for it shows a page built
|
|
1766
|
+
* entirely from the URL. Always `false` in controlled mode.
|
|
1767
|
+
*/
|
|
1768
|
+
notFound: boolean;
|
|
1752
1769
|
}
|
|
1753
1770
|
/**
|
|
1754
1771
|
* useSpareParts — fetch and paginate a machine node's spare-parts list.
|
|
@@ -4292,6 +4309,16 @@ interface MachineGridProps {
|
|
|
4292
4309
|
rootTitle?: string;
|
|
4293
4310
|
/** Language the machine tree is authored in (usually EN). Defaults to `'EN'`. */
|
|
4294
4311
|
machineLanguage?: string;
|
|
4312
|
+
/**
|
|
4313
|
+
* Other languages the tree may be authored in, tried in order when a slug
|
|
4314
|
+
* does not resolve in `machineLanguage`.
|
|
4315
|
+
*
|
|
4316
|
+
* A slug resolves only in its own language, so a half-translated tree lists a
|
|
4317
|
+
* machine by its NL slug and then cannot open it with `language: 'EN'`. Pass
|
|
4318
|
+
* the shop's locales here; `machineLanguage` and the storefront `language`
|
|
4319
|
+
* are always tried first (PWP-993).
|
|
4320
|
+
*/
|
|
4321
|
+
machineLanguages?: string[];
|
|
4295
4322
|
listing: MachineListingState;
|
|
4296
4323
|
onListingChange: (next: MachineListingState) => void;
|
|
4297
4324
|
graphqlClient?: GraphQLClient;
|
|
@@ -4331,9 +4358,19 @@ interface MachineGridProps {
|
|
|
4331
4358
|
filtersLabels?: Record<string, string>;
|
|
4332
4359
|
toolbarLabels?: Record<string, string>;
|
|
4333
4360
|
/**
|
|
4334
|
-
* Labels for the machine side of the grid.
|
|
4335
|
-
*
|
|
4336
|
-
*
|
|
4361
|
+
* Labels for the machine side of the grid.
|
|
4362
|
+
*
|
|
4363
|
+
* Keys read here: `loading`, `noMachines`, `machineNotFound`,
|
|
4364
|
+
* `quantityInMachine` and `searchParts`. The same object is passed to each
|
|
4365
|
+
* `MachineCard` as its `labels`, which reads `viewMachine` — so all six keys
|
|
4366
|
+
* belong in it.
|
|
4367
|
+
*
|
|
4368
|
+
* `quantityInMachine` and `searchParts` used to be read from `toolbarLabels`,
|
|
4369
|
+
* which is forwarded verbatim to `GridToolbar`: a shop that translated its
|
|
4370
|
+
* toolbar dictionary properly still got "Qty in machine" and "Search parts…"
|
|
4371
|
+
* in English, because those keys belong to no toolbar (PWP-995a). They are
|
|
4372
|
+
* still read from `toolbarLabels` as a fallback so existing hosts keep
|
|
4373
|
+
* working.
|
|
4337
4374
|
*/
|
|
4338
4375
|
machineCardLabels?: Record<string, string>;
|
|
4339
4376
|
/**
|
package/dist/index.js
CHANGED
|
@@ -2762,6 +2762,41 @@ function useQuickOrder(options) {
|
|
|
2762
2762
|
);
|
|
2763
2763
|
return { submitting, error, searchProducts, submit };
|
|
2764
2764
|
}
|
|
2765
|
+
|
|
2766
|
+
// src/composables/shared/utils/machineLanguage.ts
|
|
2767
|
+
var MACHINE_NOT_FOUND = "SPARE_PARTS_MACHINE_NOT_FOUND_ERROR";
|
|
2768
|
+
function isMachineNotFound(error) {
|
|
2769
|
+
const entries = error?.errors;
|
|
2770
|
+
if (!Array.isArray(entries)) return false;
|
|
2771
|
+
return entries.some(
|
|
2772
|
+
(e) => e?.extensions?.code === MACHINE_NOT_FOUND || /no machine found for slug and language/i.test(e?.message ?? "")
|
|
2773
|
+
);
|
|
2774
|
+
}
|
|
2775
|
+
function machineLanguageCandidates(machineLanguage, language, extra) {
|
|
2776
|
+
const ordered = [machineLanguage, language, ...extra ?? []];
|
|
2777
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2778
|
+
const out = [];
|
|
2779
|
+
for (const raw of ordered) {
|
|
2780
|
+
const value = raw?.trim().toUpperCase();
|
|
2781
|
+
if (!value || seen.has(value)) continue;
|
|
2782
|
+
seen.add(value);
|
|
2783
|
+
out.push(value);
|
|
2784
|
+
}
|
|
2785
|
+
return out;
|
|
2786
|
+
}
|
|
2787
|
+
async function resolveMachineAcrossLanguages(candidates, fetchOne) {
|
|
2788
|
+
for (const language of candidates) {
|
|
2789
|
+
try {
|
|
2790
|
+
const machine = await fetchOne(language);
|
|
2791
|
+
if (machine !== null && machine !== void 0) return { machine, language };
|
|
2792
|
+
} catch (error) {
|
|
2793
|
+
if (!isMachineNotFound(error)) throw error;
|
|
2794
|
+
}
|
|
2795
|
+
}
|
|
2796
|
+
return null;
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2799
|
+
// src/composables/react/useSpareParts.ts
|
|
2765
2800
|
var inflightFetches3 = /* @__PURE__ */ new Set();
|
|
2766
2801
|
var STOREFRONT_STATUSES = [
|
|
2767
2802
|
ProductStatus.A,
|
|
@@ -2778,6 +2813,8 @@ function useSpareParts(options) {
|
|
|
2778
2813
|
const [itemsFound, setItemsFound] = useState(0);
|
|
2779
2814
|
const [internalLoading, setInternalLoading] = useState(false);
|
|
2780
2815
|
const [internalPage, setInternalPage] = useState(1);
|
|
2816
|
+
const [notFound, setNotFound] = useState(false);
|
|
2817
|
+
const machineLanguagesKey = (options.machineLanguages ?? []).join(",");
|
|
2781
2818
|
const currentPage = options.page ?? internalPage;
|
|
2782
2819
|
const [totalPages, setTotalPages] = useState(1);
|
|
2783
2820
|
const fetchIdRef = useRef(0);
|
|
@@ -2843,17 +2880,35 @@ function useSpareParts(options) {
|
|
|
2843
2880
|
const filterAvailableAttributeInput = {
|
|
2844
2881
|
isSearchable: true
|
|
2845
2882
|
};
|
|
2846
|
-
const
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2883
|
+
const resolved = await resolveMachineAcrossLanguages(
|
|
2884
|
+
machineLanguageCandidates(
|
|
2885
|
+
options.machineLanguage,
|
|
2886
|
+
options.language,
|
|
2887
|
+
options.machineLanguages
|
|
2888
|
+
),
|
|
2889
|
+
(language) => service.getMachine({
|
|
2890
|
+
slug: options.slug,
|
|
2891
|
+
// The machine tree's language, NOT the parts' — see `machineLanguage`.
|
|
2892
|
+
language,
|
|
2893
|
+
sparePartsMachineProductSearchInput,
|
|
2894
|
+
filterAvailableAttributeInput,
|
|
2895
|
+
priceCalculateProductInput,
|
|
2896
|
+
imageSearchFilters: options.configuration?.imageSearchFiltersGrid,
|
|
2897
|
+
imageVariantFilters: options.configuration?.imageVariantFiltersMedium
|
|
2898
|
+
})
|
|
2899
|
+
);
|
|
2856
2900
|
if (thisId !== fetchIdRef.current) return;
|
|
2901
|
+
if (!resolved) {
|
|
2902
|
+
setNotFound(true);
|
|
2903
|
+
setInternalParts([]);
|
|
2904
|
+
setChildMachines([]);
|
|
2905
|
+
setItemsFound(0);
|
|
2906
|
+
options.onItemsFoundChange?.(0);
|
|
2907
|
+
setTotalPages(1);
|
|
2908
|
+
return;
|
|
2909
|
+
}
|
|
2910
|
+
setNotFound(false);
|
|
2911
|
+
const machine = resolved.machine;
|
|
2857
2912
|
const partsResponse = machine?.sparePartProducts;
|
|
2858
2913
|
const items = partsResponse?.items ?? [];
|
|
2859
2914
|
setInternalParts(items);
|
|
@@ -2895,6 +2950,7 @@ function useSpareParts(options) {
|
|
|
2895
2950
|
options.term,
|
|
2896
2951
|
options.language,
|
|
2897
2952
|
options.machineLanguage,
|
|
2953
|
+
machineLanguagesKey,
|
|
2898
2954
|
options.taxZone,
|
|
2899
2955
|
options.companyId,
|
|
2900
2956
|
options.textFilters,
|
|
@@ -2915,6 +2971,7 @@ function useSpareParts(options) {
|
|
|
2915
2971
|
options.term,
|
|
2916
2972
|
options.language,
|
|
2917
2973
|
options.machineLanguage,
|
|
2974
|
+
machineLanguagesKey,
|
|
2918
2975
|
options.companyId,
|
|
2919
2976
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2920
2977
|
JSON.stringify(options.textFilters ?? []),
|
|
@@ -2940,7 +2997,8 @@ function useSpareParts(options) {
|
|
|
2940
2997
|
currentPage,
|
|
2941
2998
|
totalPages,
|
|
2942
2999
|
fetchParts,
|
|
2943
|
-
goToPage
|
|
3000
|
+
goToPage,
|
|
3001
|
+
notFound: !isControlled && notFound
|
|
2944
3002
|
};
|
|
2945
3003
|
}
|
|
2946
3004
|
var inflightFetches4 = /* @__PURE__ */ new Set();
|
|
@@ -10484,13 +10542,14 @@ function MachineGrid(rawProps) {
|
|
|
10484
10542
|
// node query threads through useSpareParts.
|
|
10485
10543
|
imageVariantFilters: configuration?.imageVariantFiltersMedium
|
|
10486
10544
|
});
|
|
10487
|
-
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages } = useSpareParts({
|
|
10545
|
+
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages, notFound } = useSpareParts({
|
|
10488
10546
|
graphqlClient: props.graphqlClient,
|
|
10489
10547
|
// Idle at the root (no slug).
|
|
10490
10548
|
slug: isRoot ? void 0 : currentSlug,
|
|
10491
10549
|
term: term || void 0,
|
|
10492
10550
|
language,
|
|
10493
10551
|
machineLanguage,
|
|
10552
|
+
machineLanguages: rawProps.machineLanguages,
|
|
10494
10553
|
taxZone: props.taxZone,
|
|
10495
10554
|
user: props.user,
|
|
10496
10555
|
companyId: props.companyId,
|
|
@@ -10514,6 +10573,7 @@ function MachineGrid(rawProps) {
|
|
|
10514
10573
|
onMachineChange: setMachine
|
|
10515
10574
|
});
|
|
10516
10575
|
const machineName = machine ? getLocalizedValue(machine.name, machineLanguage) : slugToLabel(currentSlug);
|
|
10576
|
+
const machineLabel = (key, fallback) => rawProps.machineCardLabels?.[key] ?? rawProps.toolbarLabels?.[key] ?? fallback;
|
|
10517
10577
|
const partProducts = useMemo(
|
|
10518
10578
|
() => displayParts.map((p) => p.product).filter(Boolean),
|
|
10519
10579
|
[displayParts]
|
|
@@ -10588,6 +10648,12 @@ function MachineGrid(rawProps) {
|
|
|
10588
10648
|
] });
|
|
10589
10649
|
}
|
|
10590
10650
|
const hasParts = itemsFound > 0 || partProducts.length > 0;
|
|
10651
|
+
if (notFound && !partsLoading) {
|
|
10652
|
+
return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
|
|
10653
|
+
/* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "propeller-breadcrumbs mb-6", children: /* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-2 text-sm text-foreground-subtle", children: /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: basePath, className: "hover:text-primary", children: rawProps.rootTitle ?? "Machines" }) }) }) }),
|
|
10654
|
+
/* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: machineLabel("machineNotFound", "This machine could not be found.") })
|
|
10655
|
+
] });
|
|
10656
|
+
}
|
|
10591
10657
|
return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
|
|
10592
10658
|
/* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "propeller-breadcrumbs mb-6", children: /* @__PURE__ */ jsxs("ol", { className: "flex flex-wrap items-center gap-2 text-sm text-foreground-subtle", children: [
|
|
10593
10659
|
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: basePath, className: "hover:text-primary", children: rawProps.rootTitle ?? "Machines" }) }),
|
|
@@ -10640,7 +10706,9 @@ function MachineGrid(rawProps) {
|
|
|
10640
10706
|
{
|
|
10641
10707
|
defaultValue: term,
|
|
10642
10708
|
onSubmit: submitSearch,
|
|
10643
|
-
|
|
10709
|
+
searchLabel: machineLabel("searchParts", "Search parts\u2026"),
|
|
10710
|
+
searchAriaLabel: machineLabel("searchParts", "Search parts"),
|
|
10711
|
+
buttonLabel: getLabel(rawProps.toolbarLabels, "search", "Search")
|
|
10644
10712
|
},
|
|
10645
10713
|
term
|
|
10646
10714
|
),
|
|
@@ -10691,7 +10759,7 @@ function MachineGrid(rawProps) {
|
|
|
10691
10759
|
const qty = quantityBySku.get(product.sku);
|
|
10692
10760
|
if (!qty) return null;
|
|
10693
10761
|
return /* @__PURE__ */ jsxs("span", { className: "propeller-spare-part__quantity text-sm text-foreground-subtle", children: [
|
|
10694
|
-
|
|
10762
|
+
machineLabel("quantityInMachine", "Qty in machine"),
|
|
10695
10763
|
": ",
|
|
10696
10764
|
qty
|
|
10697
10765
|
] });
|
|
@@ -10723,8 +10791,8 @@ function MachinePartsSearch(props) {
|
|
|
10723
10791
|
onKeyDown: (e) => {
|
|
10724
10792
|
if (e.key === "Enter") submit();
|
|
10725
10793
|
},
|
|
10726
|
-
placeholder: props.
|
|
10727
|
-
"aria-label": props.
|
|
10794
|
+
placeholder: props.searchLabel,
|
|
10795
|
+
"aria-label": props.searchAriaLabel,
|
|
10728
10796
|
className: "w-full rounded border border-border bg-background px-3 py-2"
|
|
10729
10797
|
}
|
|
10730
10798
|
),
|
|
@@ -10734,7 +10802,7 @@ function MachinePartsSearch(props) {
|
|
|
10734
10802
|
type: "button",
|
|
10735
10803
|
onClick: submit,
|
|
10736
10804
|
className: "rounded bg-primary px-4 py-2 text-primary-foreground",
|
|
10737
|
-
children: props.
|
|
10805
|
+
children: props.buttonLabel
|
|
10738
10806
|
}
|
|
10739
10807
|
)
|
|
10740
10808
|
] });
|