@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/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,40 @@ 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
|
+
return { machine: await fetchOne(language), language };
|
|
2791
|
+
} catch (error) {
|
|
2792
|
+
if (!isMachineNotFound(error)) throw error;
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
return null;
|
|
2796
|
+
}
|
|
2797
|
+
|
|
2798
|
+
// src/composables/react/useSpareParts.ts
|
|
2765
2799
|
var inflightFetches3 = /* @__PURE__ */ new Set();
|
|
2766
2800
|
var STOREFRONT_STATUSES = [
|
|
2767
2801
|
ProductStatus.A,
|
|
@@ -2778,6 +2812,8 @@ function useSpareParts(options) {
|
|
|
2778
2812
|
const [itemsFound, setItemsFound] = useState(0);
|
|
2779
2813
|
const [internalLoading, setInternalLoading] = useState(false);
|
|
2780
2814
|
const [internalPage, setInternalPage] = useState(1);
|
|
2815
|
+
const [notFound, setNotFound] = useState(false);
|
|
2816
|
+
const machineLanguagesKey = (options.machineLanguages ?? []).join(",");
|
|
2781
2817
|
const currentPage = options.page ?? internalPage;
|
|
2782
2818
|
const [totalPages, setTotalPages] = useState(1);
|
|
2783
2819
|
const fetchIdRef = useRef(0);
|
|
@@ -2843,17 +2879,35 @@ function useSpareParts(options) {
|
|
|
2843
2879
|
const filterAvailableAttributeInput = {
|
|
2844
2880
|
isSearchable: true
|
|
2845
2881
|
};
|
|
2846
|
-
const
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2882
|
+
const resolved = await resolveMachineAcrossLanguages(
|
|
2883
|
+
machineLanguageCandidates(
|
|
2884
|
+
options.machineLanguage,
|
|
2885
|
+
options.language,
|
|
2886
|
+
options.machineLanguages
|
|
2887
|
+
),
|
|
2888
|
+
(language) => service.getMachine({
|
|
2889
|
+
slug: options.slug,
|
|
2890
|
+
// The machine tree's language, NOT the parts' — see `machineLanguage`.
|
|
2891
|
+
language,
|
|
2892
|
+
sparePartsMachineProductSearchInput,
|
|
2893
|
+
filterAvailableAttributeInput,
|
|
2894
|
+
priceCalculateProductInput,
|
|
2895
|
+
imageSearchFilters: options.configuration?.imageSearchFiltersGrid,
|
|
2896
|
+
imageVariantFilters: options.configuration?.imageVariantFiltersMedium
|
|
2897
|
+
})
|
|
2898
|
+
);
|
|
2856
2899
|
if (thisId !== fetchIdRef.current) return;
|
|
2900
|
+
if (!resolved) {
|
|
2901
|
+
setNotFound(true);
|
|
2902
|
+
setInternalParts([]);
|
|
2903
|
+
setChildMachines([]);
|
|
2904
|
+
setItemsFound(0);
|
|
2905
|
+
options.onItemsFoundChange?.(0);
|
|
2906
|
+
setTotalPages(1);
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
setNotFound(false);
|
|
2910
|
+
const machine = resolved.machine;
|
|
2857
2911
|
const partsResponse = machine?.sparePartProducts;
|
|
2858
2912
|
const items = partsResponse?.items ?? [];
|
|
2859
2913
|
setInternalParts(items);
|
|
@@ -2895,6 +2949,7 @@ function useSpareParts(options) {
|
|
|
2895
2949
|
options.term,
|
|
2896
2950
|
options.language,
|
|
2897
2951
|
options.machineLanguage,
|
|
2952
|
+
machineLanguagesKey,
|
|
2898
2953
|
options.taxZone,
|
|
2899
2954
|
options.companyId,
|
|
2900
2955
|
options.textFilters,
|
|
@@ -2915,6 +2970,7 @@ function useSpareParts(options) {
|
|
|
2915
2970
|
options.term,
|
|
2916
2971
|
options.language,
|
|
2917
2972
|
options.machineLanguage,
|
|
2973
|
+
machineLanguagesKey,
|
|
2918
2974
|
options.companyId,
|
|
2919
2975
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2920
2976
|
JSON.stringify(options.textFilters ?? []),
|
|
@@ -2940,7 +2996,8 @@ function useSpareParts(options) {
|
|
|
2940
2996
|
currentPage,
|
|
2941
2997
|
totalPages,
|
|
2942
2998
|
fetchParts,
|
|
2943
|
-
goToPage
|
|
2999
|
+
goToPage,
|
|
3000
|
+
notFound: !isControlled && notFound
|
|
2944
3001
|
};
|
|
2945
3002
|
}
|
|
2946
3003
|
var inflightFetches4 = /* @__PURE__ */ new Set();
|
|
@@ -10484,13 +10541,14 @@ function MachineGrid(rawProps) {
|
|
|
10484
10541
|
// node query threads through useSpareParts.
|
|
10485
10542
|
imageVariantFilters: configuration?.imageVariantFiltersMedium
|
|
10486
10543
|
});
|
|
10487
|
-
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages } = useSpareParts({
|
|
10544
|
+
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages, notFound } = useSpareParts({
|
|
10488
10545
|
graphqlClient: props.graphqlClient,
|
|
10489
10546
|
// Idle at the root (no slug).
|
|
10490
10547
|
slug: isRoot ? void 0 : currentSlug,
|
|
10491
10548
|
term: term || void 0,
|
|
10492
10549
|
language,
|
|
10493
10550
|
machineLanguage,
|
|
10551
|
+
machineLanguages: rawProps.machineLanguages,
|
|
10494
10552
|
taxZone: props.taxZone,
|
|
10495
10553
|
user: props.user,
|
|
10496
10554
|
companyId: props.companyId,
|
|
@@ -10514,6 +10572,7 @@ function MachineGrid(rawProps) {
|
|
|
10514
10572
|
onMachineChange: setMachine
|
|
10515
10573
|
});
|
|
10516
10574
|
const machineName = machine ? getLocalizedValue(machine.name, machineLanguage) : slugToLabel(currentSlug);
|
|
10575
|
+
const machineLabel = (key, fallback) => rawProps.machineCardLabels?.[key] ?? rawProps.toolbarLabels?.[key] ?? fallback;
|
|
10517
10576
|
const partProducts = useMemo(
|
|
10518
10577
|
() => displayParts.map((p) => p.product).filter(Boolean),
|
|
10519
10578
|
[displayParts]
|
|
@@ -10588,6 +10647,12 @@ function MachineGrid(rawProps) {
|
|
|
10588
10647
|
] });
|
|
10589
10648
|
}
|
|
10590
10649
|
const hasParts = itemsFound > 0 || partProducts.length > 0;
|
|
10650
|
+
if (notFound && !partsLoading) {
|
|
10651
|
+
return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
|
|
10652
|
+
/* @__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" }) }) }) }),
|
|
10653
|
+
/* @__PURE__ */ jsx("p", { className: "py-12 text-center text-foreground-subtle", children: machineLabel("machineNotFound", "This machine could not be found.") })
|
|
10654
|
+
] });
|
|
10655
|
+
}
|
|
10591
10656
|
return /* @__PURE__ */ jsxs("div", { className: rawProps.className, children: [
|
|
10592
10657
|
/* @__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
10658
|
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("a", { href: basePath, className: "hover:text-primary", children: rawProps.rootTitle ?? "Machines" }) }),
|
|
@@ -10640,7 +10705,9 @@ function MachineGrid(rawProps) {
|
|
|
10640
10705
|
{
|
|
10641
10706
|
defaultValue: term,
|
|
10642
10707
|
onSubmit: submitSearch,
|
|
10643
|
-
|
|
10708
|
+
searchLabel: machineLabel("searchParts", "Search parts\u2026"),
|
|
10709
|
+
searchAriaLabel: machineLabel("searchParts", "Search parts"),
|
|
10710
|
+
buttonLabel: getLabel(rawProps.toolbarLabels, "search", "Search")
|
|
10644
10711
|
},
|
|
10645
10712
|
term
|
|
10646
10713
|
),
|
|
@@ -10691,7 +10758,7 @@ function MachineGrid(rawProps) {
|
|
|
10691
10758
|
const qty = quantityBySku.get(product.sku);
|
|
10692
10759
|
if (!qty) return null;
|
|
10693
10760
|
return /* @__PURE__ */ jsxs("span", { className: "propeller-spare-part__quantity text-sm text-foreground-subtle", children: [
|
|
10694
|
-
|
|
10761
|
+
machineLabel("quantityInMachine", "Qty in machine"),
|
|
10695
10762
|
": ",
|
|
10696
10763
|
qty
|
|
10697
10764
|
] });
|
|
@@ -10723,8 +10790,8 @@ function MachinePartsSearch(props) {
|
|
|
10723
10790
|
onKeyDown: (e) => {
|
|
10724
10791
|
if (e.key === "Enter") submit();
|
|
10725
10792
|
},
|
|
10726
|
-
placeholder: props.
|
|
10727
|
-
"aria-label": props.
|
|
10793
|
+
placeholder: props.searchLabel,
|
|
10794
|
+
"aria-label": props.searchAriaLabel,
|
|
10728
10795
|
className: "w-full rounded border border-border bg-background px-3 py-2"
|
|
10729
10796
|
}
|
|
10730
10797
|
),
|
|
@@ -10734,7 +10801,7 @@ function MachinePartsSearch(props) {
|
|
|
10734
10801
|
type: "button",
|
|
10735
10802
|
onClick: submit,
|
|
10736
10803
|
className: "rounded bg-primary px-4 py-2 text-primary-foreground",
|
|
10737
|
-
children: props.
|
|
10804
|
+
children: props.buttonLabel
|
|
10738
10805
|
}
|
|
10739
10806
|
)
|
|
10740
10807
|
] });
|