@propeller-commerce/propeller-v2-react-ui 0.20.1 → 0.22.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 +101 -0
- package/dist/index.cjs +108 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +592 -525
- package/dist/index.d.ts +592 -525
- package/dist/index.js +110 -55
- package/dist/index.js.map +1 -1
- package/dist/pure.cjs +6 -14
- package/dist/pure.cjs.map +1 -1
- package/dist/pure.d.cts +12 -0
- package/dist/pure.d.ts +12 -0
- package/dist/pure.js +7 -15
- package/dist/pure.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,107 @@ 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.22.0] - 2026-09-24
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- **Media language is matched case-insensitively.** The API lowercases
|
|
16
|
+
`documents[].language` while leaving `alt[].language` as uploaded, so
|
|
17
|
+
`ProductDownloads` matched the label but never the file and served
|
|
18
|
+
`documents[0]` — the Dutch PDF under an English label. `ProductVideos` had
|
|
19
|
+
the identical comparison and worked only by data luck. Also fixed in
|
|
20
|
+
`useProductSearch.filterByLanguage`, where a case mismatch dropped **every**
|
|
21
|
+
product from the grid, and in `DefaultProductImage`. (PWP-984)
|
|
22
|
+
- **`checkoutAllowed` is usable, and fails closed.** It read the raw `companyId`
|
|
23
|
+
option rather than the resolved one, so a contact acting for their own company
|
|
24
|
+
matched no authorization config and every cart reported as within limit; and it
|
|
25
|
+
evaluated its own cart state, which stays null when the consumer only seeds a
|
|
26
|
+
`cartId`. The hook now hydrates a seeded cart, and reports `false` while that
|
|
27
|
+
is in flight rather than `true`. (PWP-988)
|
|
28
|
+
- **The place-order button is gated on the authorization limit.** An over-limit
|
|
29
|
+
purchaser opening checkout directly got an ungated button and a backend
|
|
30
|
+
rejection; `CartOverview` now shows an `authorizationRequired` message
|
|
31
|
+
instead, using the same predicate as the cart summary. (PWP-988)
|
|
32
|
+
- **Semi-closed surfaces no longer flash their logged-out state** for a signed-in
|
|
33
|
+
visitor while the profile loads. Every `isContentHidden` call site passes the
|
|
34
|
+
provider’s `isAuthenticated` (core-ui 0.9.0+). (PWP-989)
|
|
35
|
+
- **The cart sidebar slides again.** The overflow fix named only `transform` in
|
|
36
|
+
its transition, but Tailwind v4 emits `translate-x-*` as the `translate`
|
|
37
|
+
property, so the panel jumped. (PWP-1002)
|
|
38
|
+
- **Header triggers inherit their colour.** `AccountIconAndMenu` and
|
|
39
|
+
`CompanySwitcher` hardcoded `text-white`, which broke light headers;
|
|
40
|
+
`text-foreground` then broke dark ones. Both now use `text-inherit`, with the
|
|
41
|
+
hover tint derived from `currentColor`. (PWP-1005)
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- `CompanySwitcher` accepts `triggerClassName`; it had no class prop at all.
|
|
46
|
+
- `ProductInfo` accepts `cartId`, `createCart` and `onCartCreated`, and forwards
|
|
47
|
+
them to both the injected and the default add-to-cart. It declared none of
|
|
48
|
+
them, so the PDP’s add-to-cart always failed for a visitor without a cart.
|
|
49
|
+
`ProductCard` forwards the same to an injected component. (PWP-986)
|
|
50
|
+
- `CartOverview` resolves `user` and `companyId` from `PropellerProvider`.
|
|
51
|
+
|
|
52
|
+
## [0.21.0] - 2026-09-23
|
|
53
|
+
|
|
54
|
+
`MachineGrid` stops losing rows, and becomes usable in a translated or
|
|
55
|
+
customised storefront. One project had given up on it and rebuilt the surface
|
|
56
|
+
by hand (~450 lines), losing the breadcrumb, module grid and filter wiring.
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
|
|
60
|
+
- **A machine with no slug in the tree language is no longer dropped from the
|
|
61
|
+
list.** `childSlugHref` returned `null` and the card was filtered out, so a
|
|
62
|
+
customer saw three of four machines with nothing saying so — and the same
|
|
63
|
+
helper guards the child-module grid, so a module could vanish from a machine
|
|
64
|
+
the same way. Two changes: `ROOT_MACHINE_FIELDS` no longer passes
|
|
65
|
+
`(language: $language)` to `name` / `description` / `slug`, so the
|
|
66
|
+
cross-language fallback in `getLocalizedValue` has entries to fall back to;
|
|
67
|
+
and a machine with no slug in *any* language now renders as an unlinked card
|
|
68
|
+
rather than vanishing. Needs SDK ≥ 0.17.0 for the child-machine half, which
|
|
69
|
+
comes through `getMachine`. (PWP-993)
|
|
70
|
+
|
|
71
|
+
The asymmetry that made this hard to spot: the **name** falls back across
|
|
72
|
+
languages, the **slug** did not — so a half-translated tree rendered names in
|
|
73
|
+
the wrong language rather than failing, but lost whole rows when the slug was
|
|
74
|
+
the missing half.
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- **`MachineGrid` forwards label overrides to the parts list.** It rendered its
|
|
79
|
+
parts through `ProductGrid` but forwarded none of `productCardLabels`,
|
|
80
|
+
`addToCartLabels`, `stockLabels`, `priceLabels` or `labels`, and there was no
|
|
81
|
+
way to reach them from outside — so "In stock", "Add" and "Search parts…"
|
|
82
|
+
appeared in English in the middle of translated copy. A monolingual shop
|
|
83
|
+
never noticed. (PWP-995a)
|
|
84
|
+
- **`MachineGrid.productCardComponent`** — the `ProductGrid` prop of the same
|
|
85
|
+
name, forwarded. Without it a quote-only product rendered with the host's
|
|
86
|
+
custom card on a normal listing and with the stock card inside a machine, so
|
|
87
|
+
the same product looked different depending on the page it was reached from.
|
|
88
|
+
(PWP-995c)
|
|
89
|
+
- **`useSpareParts({ page })`** — a controlled page, mirroring the existing
|
|
90
|
+
controlled `parts`. Every other listing input (`term`, `textFilters`,
|
|
91
|
+
`sortField`, `pageSize`) was already an option, so the absence of `page` read
|
|
92
|
+
as "paging is internal" — and it was not: driving the hook from URL state and
|
|
93
|
+
rendering `<GridPagination>` changed the URL and re-rendered the same first
|
|
94
|
+
twelve parts. The only way in was a `useEffect(() => goToPage(page))` that
|
|
95
|
+
`MachineGrid` did internally and nothing documented. `goToPage` still works
|
|
96
|
+
for uncontrolled hosts; omit `page` for the previous behaviour. (PWP-995b)
|
|
97
|
+
|
|
98
|
+
### Changed
|
|
99
|
+
|
|
100
|
+
- `MachineCard.href` is now optional — a machine with no URL renders as a
|
|
101
|
+
`<div>` instead of an `<a>`.
|
|
102
|
+
- **`MachineGrid` drives `useSpareParts` with `page` directly** instead of the
|
|
103
|
+
internal `goToPage` effect.
|
|
104
|
+
|
|
105
|
+
### Documentation
|
|
106
|
+
|
|
107
|
+
- **`MachineCardProps.labels` documented one key while `MachineGrid` read
|
|
108
|
+
three.** The doc named only `viewMachine`; `MachineGrid` also reads `loading`
|
|
109
|
+
and `noMachines` off the same object, so anyone following the doc left those
|
|
110
|
+
two in English. Both sides now say so. (PWP-995d)
|
|
111
|
+
|
|
11
112
|
## [0.20.1] - 2026-09-23
|
|
12
113
|
|
|
13
114
|
### Fixed
|
package/dist/index.cjs
CHANGED
|
@@ -767,10 +767,29 @@ function useCart(options) {
|
|
|
767
767
|
const [loading, setLoading] = React6.useState(false);
|
|
768
768
|
const [error, setError] = React6.useState(null);
|
|
769
769
|
const notesTimers = React6.useRef({});
|
|
770
|
-
|
|
771
|
-
(
|
|
772
|
-
|
|
773
|
-
|
|
770
|
+
React6.useEffect(() => {
|
|
771
|
+
if (!graphqlClient || !cartId || cart) return;
|
|
772
|
+
let cancelled = false;
|
|
773
|
+
(async () => {
|
|
774
|
+
try {
|
|
775
|
+
const fetched = await propellerV2CoreUi.createServices(graphqlClient).cart.getCart({
|
|
776
|
+
cartId,
|
|
777
|
+
imageSearchFilters: imageSearchFilters(),
|
|
778
|
+
imageVariantFilters: imageVariantFilters(),
|
|
779
|
+
language
|
|
780
|
+
});
|
|
781
|
+
if (!cancelled && fetched) setCart(fetched);
|
|
782
|
+
} catch {
|
|
783
|
+
}
|
|
784
|
+
})();
|
|
785
|
+
return () => {
|
|
786
|
+
cancelled = true;
|
|
787
|
+
};
|
|
788
|
+
}, [graphqlClient, cartId, cart, language, imageSearchFilters, imageVariantFilters]);
|
|
789
|
+
const checkoutAllowed = React6.useMemo(() => {
|
|
790
|
+
if (cartId && !cart) return false;
|
|
791
|
+
return propellerV2CoreUi.isCheckoutAllowed(user, resolvedCompanyId, cart);
|
|
792
|
+
}, [user, resolvedCompanyId, cart, cartId]);
|
|
774
793
|
function getMinQuantity(product) {
|
|
775
794
|
const min = product?.minimumQuantity;
|
|
776
795
|
return min && min > 0 ? min : 1;
|
|
@@ -1591,7 +1610,8 @@ var INFRA_KEYS = [
|
|
|
1591
1610
|
"includeTax",
|
|
1592
1611
|
"currency",
|
|
1593
1612
|
"configuration",
|
|
1594
|
-
"portalMode"
|
|
1613
|
+
"portalMode",
|
|
1614
|
+
"isAuthenticated"
|
|
1595
1615
|
];
|
|
1596
1616
|
function useInfraProps(props) {
|
|
1597
1617
|
const ctx = usePropellerContext();
|
|
@@ -2268,10 +2288,11 @@ function useProductSearch(options) {
|
|
|
2268
2288
|
const isLoading = !isControlled && internalLoading;
|
|
2269
2289
|
function filterByLanguage(products, lang) {
|
|
2270
2290
|
if (!lang) return products;
|
|
2291
|
+
const target = lang.toUpperCase();
|
|
2271
2292
|
return products.filter((p) => {
|
|
2272
2293
|
const names = p.names || p.names || [];
|
|
2273
2294
|
if (!names || names.length === 0) return true;
|
|
2274
|
-
return names.some((n) => n.language ===
|
|
2295
|
+
return names.some((n) => (n.language || "").toUpperCase() === target);
|
|
2275
2296
|
});
|
|
2276
2297
|
}
|
|
2277
2298
|
const fetchProducts = React6.useCallback(async () => {
|
|
@@ -2776,7 +2797,8 @@ function useSpareParts(options) {
|
|
|
2776
2797
|
const [childMachines, setChildMachines] = React6.useState([]);
|
|
2777
2798
|
const [itemsFound, setItemsFound] = React6.useState(0);
|
|
2778
2799
|
const [internalLoading, setInternalLoading] = React6.useState(false);
|
|
2779
|
-
const [
|
|
2800
|
+
const [internalPage, setInternalPage] = React6.useState(1);
|
|
2801
|
+
const currentPage = options.page ?? internalPage;
|
|
2780
2802
|
const [totalPages, setTotalPages] = React6.useState(1);
|
|
2781
2803
|
const fetchIdRef = React6.useRef(0);
|
|
2782
2804
|
const displayParts = React6.useMemo(
|
|
@@ -2925,7 +2947,7 @@ function useSpareParts(options) {
|
|
|
2925
2947
|
userKey
|
|
2926
2948
|
]);
|
|
2927
2949
|
const goToPage = React6.useCallback((page) => {
|
|
2928
|
-
|
|
2950
|
+
setInternalPage((prev) => {
|
|
2929
2951
|
if (page >= 1 && (totalPages <= 1 || page <= totalPages)) return page;
|
|
2930
2952
|
return prev;
|
|
2931
2953
|
});
|
|
@@ -2944,9 +2966,9 @@ function useSpareParts(options) {
|
|
|
2944
2966
|
var inflightFetches4 = /* @__PURE__ */ new Set();
|
|
2945
2967
|
var ROOT_MACHINE_FIELDS = `
|
|
2946
2968
|
id
|
|
2947
|
-
name
|
|
2948
|
-
description
|
|
2949
|
-
slug
|
|
2969
|
+
name { language value }
|
|
2970
|
+
description { language value }
|
|
2971
|
+
slug { language value }
|
|
2950
2972
|
media {
|
|
2951
2973
|
images {
|
|
2952
2974
|
items { imageVariants(input: $imageVariantFilters) { url } }
|
|
@@ -4227,7 +4249,7 @@ function AccountIconAndMenu(rawProps) {
|
|
|
4227
4249
|
onClick: (event) => handleIconClick(),
|
|
4228
4250
|
"aria-label": propellerV2CoreUi.getLabel(props.labels, "accountLabel", "Account"),
|
|
4229
4251
|
"data-open": menuOpen ? "true" : "false",
|
|
4230
|
-
className: cn(`propeller-account-menu__trigger inline-flex items-center gap-2 px-3 py-2 rounded-control text-sm font-medium transition-colors text-
|
|
4252
|
+
className: cn(`propeller-account-menu__trigger inline-flex items-center gap-2 px-3 py-2 rounded-control text-sm font-medium transition-colors text-inherit${props.iconClassName ? " " + props.iconClassName : ""}`),
|
|
4231
4253
|
children: [
|
|
4232
4254
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4233
4255
|
"svg",
|
|
@@ -7639,7 +7661,8 @@ function CartIconAndSidebar(rawProps) {
|
|
|
7639
7661
|
] });
|
|
7640
7662
|
}
|
|
7641
7663
|
var CartIconAndSidebar_default = CartIconAndSidebar;
|
|
7642
|
-
function CartOverview(
|
|
7664
|
+
function CartOverview(rawProps) {
|
|
7665
|
+
const props = useInfraProps(rawProps);
|
|
7643
7666
|
const [reference, setReference] = React6.useState("");
|
|
7644
7667
|
const [notes, setNotes] = React6.useState("");
|
|
7645
7668
|
const [termsAccepted, setTermsAccepted] = React6.useState(false);
|
|
@@ -7649,6 +7672,7 @@ function CartOverview(props) {
|
|
|
7649
7672
|
const showReference = props.showReference !== false;
|
|
7650
7673
|
const showTermsAndConditions = props.showTermsAndConditions !== false;
|
|
7651
7674
|
const showPurchaseButton = props.showPurchaseButton !== false;
|
|
7675
|
+
const overLimit = propellerV2CoreUi.isOverAuthorizationLimit(props.user, props.companyId, props.cart);
|
|
7652
7676
|
const invoiceAddress = props.cart?.invoiceAddress;
|
|
7653
7677
|
const deliveryAddress = props.cart?.deliveryAddress;
|
|
7654
7678
|
const getCountryName = (code) => propellerV2CoreUi.getCountryName(code, props.countries);
|
|
@@ -7788,7 +7812,12 @@ function CartOverview(props) {
|
|
|
7788
7812
|
] });
|
|
7789
7813
|
})() })
|
|
7790
7814
|
] }) : null,
|
|
7791
|
-
showPurchaseButton ? /* @__PURE__ */ jsxRuntime.
|
|
7815
|
+
showPurchaseButton && overLimit ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "propeller-cart-overview__authorization-required text-sm text-muted-foreground mt-2", children: propellerV2CoreUi.getLabel(
|
|
7816
|
+
props.labels,
|
|
7817
|
+
"authorizationRequired",
|
|
7818
|
+
"This order exceeds your authorization limit. Request authorization from the cart to continue."
|
|
7819
|
+
) }) : null,
|
|
7820
|
+
showPurchaseButton && !overLimit ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7792
7821
|
"button",
|
|
7793
7822
|
{
|
|
7794
7823
|
type: "button",
|
|
@@ -8163,6 +8192,7 @@ var RESOLVE_SPEC = {
|
|
|
8163
8192
|
currency: { infra: "currency", default: "\u20AC" },
|
|
8164
8193
|
user: { infra: "user", default: null },
|
|
8165
8194
|
portalMode: { infra: "portalMode" },
|
|
8195
|
+
isAuthenticated: { infra: "isAuthenticated" },
|
|
8166
8196
|
columns: { grid: "columns", default: 3 },
|
|
8167
8197
|
showPrice: { grid: "showPrice" },
|
|
8168
8198
|
showStock: { grid: "showStock" },
|
|
@@ -8206,7 +8236,7 @@ function ClusterCard(rawProps) {
|
|
|
8206
8236
|
const textLabelValues = resolveAttributeValues(cluster, props.textLabels).map((value) => ({
|
|
8207
8237
|
value
|
|
8208
8238
|
}));
|
|
8209
|
-
const contentHidden = propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
8239
|
+
const contentHidden = propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
8210
8240
|
const defaultProductInventory = contentHidden ? void 0 : cluster?.defaultProduct?.inventory;
|
|
8211
8241
|
const useTax = props.includeTax !== void 0 ? !!props.includeTax : false;
|
|
8212
8242
|
const priceObj = contentHidden ? void 0 : cluster?.defaultProduct?.price;
|
|
@@ -8549,6 +8579,7 @@ function getMachineImageUrl(machine) {
|
|
|
8549
8579
|
}
|
|
8550
8580
|
function MachineCard(props) {
|
|
8551
8581
|
const { machine, href, language } = props;
|
|
8582
|
+
const Wrapper = href ? "a" : "div";
|
|
8552
8583
|
const showImage = props.showImage ?? true;
|
|
8553
8584
|
const showDescription = props.showDescription ?? false;
|
|
8554
8585
|
const name = propellerV2CoreUi.getLocalizedValue(machine.name, language);
|
|
@@ -8560,10 +8591,9 @@ function MachineCard(props) {
|
|
|
8560
8591
|
{
|
|
8561
8592
|
className: cn(`propeller-machine-card group relative flex h-full flex-col overflow-hidden rounded-lg border border-border bg-background ${props.className ?? ""}`),
|
|
8562
8593
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
8563
|
-
|
|
8594
|
+
Wrapper,
|
|
8564
8595
|
{
|
|
8565
|
-
href,
|
|
8566
|
-
onClick: handleClick,
|
|
8596
|
+
...href ? { href, onClick: handleClick } : {},
|
|
8567
8597
|
className: "flex h-full flex-col focus:outline-none focus-visible:ring-2 focus-visible:ring-primary",
|
|
8568
8598
|
children: [
|
|
8569
8599
|
showImage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-machine-card__image aspect-square w-full overflow-hidden bg-background-subtle", children: imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -8633,7 +8663,7 @@ function LoginToOrderButton(props) {
|
|
|
8633
8663
|
var LoginToOrderButton_default = LoginToOrderButton;
|
|
8634
8664
|
function ProductPriceDisplay(props) {
|
|
8635
8665
|
function isHidden2() {
|
|
8636
|
-
return propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
8666
|
+
return propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
8637
8667
|
}
|
|
8638
8668
|
function formatPrice17(value) {
|
|
8639
8669
|
if (value === null || value === void 0) return "";
|
|
@@ -8725,6 +8755,7 @@ var RESOLVE_SPEC2 = {
|
|
|
8725
8755
|
language: { infra: "language", default: "NL" },
|
|
8726
8756
|
includeTax: { infra: "includeTax" },
|
|
8727
8757
|
portalMode: { infra: "portalMode" },
|
|
8758
|
+
isAuthenticated: { infra: "isAuthenticated" },
|
|
8728
8759
|
configuration: { infra: "configuration" },
|
|
8729
8760
|
// Same entry ClusterCard has always had. Without it a shop priced in
|
|
8730
8761
|
// anything but euros had no route to the cards at all: the grid resolved the
|
|
@@ -8783,7 +8814,7 @@ function ProductCardRoot(rawProps) {
|
|
|
8783
8814
|
const language = props.language || "NL";
|
|
8784
8815
|
const product = props.product;
|
|
8785
8816
|
const isRow = props.columns === 1;
|
|
8786
|
-
const contentHidden = propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
8817
|
+
const contentHidden = propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
8787
8818
|
const showStock = !!props.showStock && !contentHidden;
|
|
8788
8819
|
const showCta = props.allowAddToCart !== false || contentHidden;
|
|
8789
8820
|
const name = getProductName(product, language);
|
|
@@ -9080,6 +9111,7 @@ function ProductCardPrice(props) {
|
|
|
9080
9111
|
currency: resolved.currency,
|
|
9081
9112
|
language: resolved.language,
|
|
9082
9113
|
portalMode: resolved.portalMode,
|
|
9114
|
+
isAuthenticated: resolved.isAuthenticated,
|
|
9083
9115
|
user: resolved.user,
|
|
9084
9116
|
showLoginPrompt: false
|
|
9085
9117
|
}
|
|
@@ -9087,7 +9119,7 @@ function ProductCardPrice(props) {
|
|
|
9087
9119
|
}
|
|
9088
9120
|
function ProductCardAddToCart(props) {
|
|
9089
9121
|
const { product, resolved } = useProductCard();
|
|
9090
|
-
if (propellerV2CoreUi.isContentHidden(resolved.portalMode, resolved.user)) {
|
|
9122
|
+
if (propellerV2CoreUi.isContentHidden(resolved.portalMode, resolved.user, resolved.isAuthenticated)) {
|
|
9091
9123
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
9092
9124
|
LoginToOrderButton_default,
|
|
9093
9125
|
{
|
|
@@ -9108,9 +9140,13 @@ function ProductCardAddToCart(props) {
|
|
|
9108
9140
|
allowIncrDecr: resolved.allowIncrDecr,
|
|
9109
9141
|
showModal: resolved.showModal,
|
|
9110
9142
|
enableStockValidation: resolved.enableStockValidation,
|
|
9143
|
+
onAddToCart: resolved.onAddToCart,
|
|
9111
9144
|
afterAddToCart: resolved.afterAddToCart,
|
|
9112
9145
|
onProceedToCheckout: resolved.onProceedToCheckout,
|
|
9113
9146
|
onRequestQuoteClick: resolved.onRequestQuoteClick,
|
|
9147
|
+
createCart: resolved.createCart,
|
|
9148
|
+
onCartCreated: resolved.onCartCreated,
|
|
9149
|
+
includeTax: resolved.includeTax,
|
|
9114
9150
|
labels: resolved.addToCartLabels
|
|
9115
9151
|
}
|
|
9116
9152
|
);
|
|
@@ -9233,7 +9269,7 @@ function ProductGrid(rawProps) {
|
|
|
9233
9269
|
}
|
|
9234
9270
|
function showAddToCart() {
|
|
9235
9271
|
const allow = props.allowAddToCart !== false;
|
|
9236
|
-
return !propellerV2CoreUi.isContentHidden(props.portalMode, props.user) && allow;
|
|
9272
|
+
return !propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
|
|
9237
9273
|
}
|
|
9238
9274
|
function getSkeletonItems() {
|
|
9239
9275
|
const cols = props.columns || 3;
|
|
@@ -9511,7 +9547,7 @@ function GridFilters(rawProps) {
|
|
|
9511
9547
|
}
|
|
9512
9548
|
function showAvailability() {
|
|
9513
9549
|
if (!props.showAvailabilityFilter) return false;
|
|
9514
|
-
return !propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
9550
|
+
return !propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
9515
9551
|
}
|
|
9516
9552
|
function getFilterName(filter) {
|
|
9517
9553
|
return filter?.attributeDescription?.name || "";
|
|
@@ -10066,7 +10102,7 @@ function GridToolbar(rawProps) {
|
|
|
10066
10102
|
return badges;
|
|
10067
10103
|
}
|
|
10068
10104
|
function isPriceSortDisabled() {
|
|
10069
|
-
return propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
10105
|
+
return propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
10070
10106
|
}
|
|
10071
10107
|
function getAvailabilityLabel() {
|
|
10072
10108
|
const qty = props.minStock;
|
|
@@ -10468,7 +10504,7 @@ function MachineGrid(rawProps) {
|
|
|
10468
10504
|
// node query threads through useSpareParts.
|
|
10469
10505
|
imageVariantFilters: configuration?.imageVariantFiltersMedium
|
|
10470
10506
|
});
|
|
10471
|
-
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages
|
|
10507
|
+
const { displayParts, childMachines, isLoading: partsLoading, currentPage, totalPages } = useSpareParts({
|
|
10472
10508
|
graphqlClient: props.graphqlClient,
|
|
10473
10509
|
// Idle at the root (no slug).
|
|
10474
10510
|
slug: isRoot ? void 0 : currentSlug,
|
|
@@ -10484,6 +10520,10 @@ function MachineGrid(rawProps) {
|
|
|
10484
10520
|
sortField,
|
|
10485
10521
|
sortOrder,
|
|
10486
10522
|
pageSize: offset,
|
|
10523
|
+
// The hook takes the controlled page directly now. It used to own its own
|
|
10524
|
+
// counter, so the only way to drive it from URL state was the effect
|
|
10525
|
+
// below — undiscoverable unless you read this file (PWP-995b).
|
|
10526
|
+
page: listing.page,
|
|
10487
10527
|
configuration: rawProps.configuration ?? props.configuration,
|
|
10488
10528
|
onFiltersChange: setGridFilters,
|
|
10489
10529
|
onPriceBoundsChange: (min, max) => {
|
|
@@ -10493,9 +10533,6 @@ function MachineGrid(rawProps) {
|
|
|
10493
10533
|
onItemsFoundChange: setItemsFound,
|
|
10494
10534
|
onMachineChange: setMachine
|
|
10495
10535
|
});
|
|
10496
|
-
React6.useEffect(() => {
|
|
10497
|
-
goToPage(listing.page);
|
|
10498
|
-
}, [listing.page, goToPage]);
|
|
10499
10536
|
const machineName = machine ? propellerV2CoreUi.getLocalizedValue(machine.name, machineLanguage) : slugToLabel(currentSlug);
|
|
10500
10537
|
const partProducts = React6.useMemo(
|
|
10501
10538
|
() => displayParts.map((p) => p.product).filter(Boolean),
|
|
@@ -10552,11 +10589,10 @@ function MachineGrid(rawProps) {
|
|
|
10552
10589
|
);
|
|
10553
10590
|
const childSlugHref = (child) => {
|
|
10554
10591
|
const slug = propellerV2CoreUi.getLocalizedValue(child.slug, machineLanguage);
|
|
10555
|
-
|
|
10556
|
-
return `${currentPath}/${slug}`;
|
|
10592
|
+
return slug ? `${currentPath}/${slug}` : void 0;
|
|
10557
10593
|
};
|
|
10558
10594
|
if (isRoot) {
|
|
10559
|
-
const cards = rootMachines.map((m) => ({ machine: m, href: childSlugHref(m) }))
|
|
10595
|
+
const cards = rootMachines.map((m) => ({ machine: m, href: childSlugHref(m) }));
|
|
10560
10596
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: rawProps.className, children: [
|
|
10561
10597
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "propeller-grid-title mb-6 text-2xl font-semibold", children: rawProps.rootTitle ?? "Machines" }),
|
|
10562
10598
|
rootLoading ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-12 text-center text-foreground-subtle", children: propellerV2CoreUi.getLabel(rawProps.machineCardLabels, "loading", "Loading\u2026") }) : cards.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-12 text-center text-foreground-subtle", children: propellerV2CoreUi.getLabel(rawProps.machineCardLabels, "noMachines", "No machines found.") }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: cards.map(({ machine: m, href }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -10588,7 +10624,6 @@ function MachineGrid(rawProps) {
|
|
|
10588
10624
|
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "propeller-grid-title mb-6 text-2xl font-semibold", children: machineName }),
|
|
10589
10625
|
childMachines.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "propeller-machine-children mb-8 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4", children: childMachines.map((child) => {
|
|
10590
10626
|
const href = childSlugHref(child);
|
|
10591
|
-
if (!href) return null;
|
|
10592
10627
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10593
10628
|
MachineCard,
|
|
10594
10629
|
{
|
|
@@ -10666,6 +10701,12 @@ function MachineGrid(rawProps) {
|
|
|
10666
10701
|
columns: viewMode === "list" ? 1 : 3,
|
|
10667
10702
|
showAvailability: rawProps.showAvailability ?? false,
|
|
10668
10703
|
showStock: rawProps.showStock ?? true,
|
|
10704
|
+
productCardComponent: rawProps.productCardComponent,
|
|
10705
|
+
productCardLabels: rawProps.productCardLabels,
|
|
10706
|
+
addToCartLabels: rawProps.addToCartLabels,
|
|
10707
|
+
stockLabels: rawProps.stockLabels,
|
|
10708
|
+
priceLabels: rawProps.priceLabels,
|
|
10709
|
+
labels: rawProps.labels,
|
|
10669
10710
|
belowName: (product) => {
|
|
10670
10711
|
const qty = quantityBySku.get(product.sku);
|
|
10671
10712
|
if (!qty) return null;
|
|
@@ -10914,7 +10955,7 @@ function ClusterOptions(rawProps) {
|
|
|
10914
10955
|
const language = props.language;
|
|
10915
10956
|
const options = props.options || [];
|
|
10916
10957
|
const sel = selectedProductIds;
|
|
10917
|
-
const hidePrices = propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
10958
|
+
const hidePrices = propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
10918
10959
|
return options.filter((option) => option.hidden !== propellerSdkV2.YesNo.Y).map((option) => {
|
|
10919
10960
|
const idStr = option.id.toString();
|
|
10920
10961
|
const selectedProductId = sel[idStr] || "";
|
|
@@ -11098,7 +11139,11 @@ function CompanySwitcher(props) {
|
|
|
11098
11139
|
type: "button",
|
|
11099
11140
|
"aria-haspopup": "listbox",
|
|
11100
11141
|
"aria-label": propellerV2CoreUi.getLabel(props.labels, "switchCompanyAriaLabel", "Switch company"),
|
|
11101
|
-
className:
|
|
11142
|
+
className: cn(
|
|
11143
|
+
// `text-inherit`: this sits in the host's header, which may be dark or
|
|
11144
|
+
// light. See the note on AccountIconAndMenu's trigger.
|
|
11145
|
+
`propeller-company-switcher__trigger flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium transition-colors text-inherit${props.triggerClassName ? " " + props.triggerClassName : ""}`
|
|
11146
|
+
),
|
|
11102
11147
|
onClick: (event) => toggleDropdown(),
|
|
11103
11148
|
"aria-expanded": isOpen,
|
|
11104
11149
|
children: [
|
|
@@ -15073,7 +15118,7 @@ function PriceToggle(props) {
|
|
|
15073
15118
|
var PriceToggle_default = PriceToggle;
|
|
15074
15119
|
function ProductBulkPrices(props) {
|
|
15075
15120
|
const includeTax = !!props.includeTax;
|
|
15076
|
-
const isHidden2 = propellerV2CoreUi.isContentHidden(props.portalMode, props.user);
|
|
15121
|
+
const isHidden2 = propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
15077
15122
|
function getTierQuantity(tier) {
|
|
15078
15123
|
const discount = tier.discount;
|
|
15079
15124
|
return discount?.quantityFrom ?? tier.quantity ?? null;
|
|
@@ -15620,14 +15665,10 @@ function ProductDownloads(props) {
|
|
|
15620
15665
|
const hasItems = items.length > 0;
|
|
15621
15666
|
const lang = props.language || "NL";
|
|
15622
15667
|
function getDocumentUrl(doc) {
|
|
15623
|
-
|
|
15624
|
-
const match = docs.find((d) => d.language === lang);
|
|
15625
|
-
return match?.originalUrl || docs?.[0]?.originalUrl || "";
|
|
15668
|
+
return propellerV2CoreUi.resolveLanguageEntry(doc.documents, lang)?.originalUrl || "";
|
|
15626
15669
|
}
|
|
15627
15670
|
function getDocumentName(doc) {
|
|
15628
|
-
|
|
15629
|
-
const match = alts.find((a) => a.language === lang);
|
|
15630
|
-
return match?.value || alts?.[0]?.value || "Download";
|
|
15671
|
+
return propellerV2CoreUi.getLanguageString(doc.alt, lang, "Download");
|
|
15631
15672
|
}
|
|
15632
15673
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn(`propeller-product-downloads ${props.className || ""}`), children: [
|
|
15633
15674
|
hasItems ? /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "propeller-product-downloads__title text-base font-semibold text-foreground mb-3", children: propellerV2CoreUi.getLabel(props.labels, "title", "Downloads") }) : null,
|
|
@@ -15833,12 +15874,14 @@ function ProductGallery(props) {
|
|
|
15833
15874
|
var ProductGallery_default = ProductGallery;
|
|
15834
15875
|
function pickImageUrl(mediaItems, target) {
|
|
15835
15876
|
if (!mediaItems || mediaItems.length === 0) return null;
|
|
15877
|
+
const targetUpper = (target || "").toUpperCase();
|
|
15878
|
+
const langMatches = (value) => (value || "").toUpperCase() === targetUpper;
|
|
15836
15879
|
for (const m of mediaItems) {
|
|
15837
|
-
const match = m.imageVariants?.find((v) => v.language
|
|
15880
|
+
const match = m.imageVariants?.find((v) => langMatches(v.language) && v.url);
|
|
15838
15881
|
if (match?.url) return match.url;
|
|
15839
15882
|
}
|
|
15840
15883
|
for (const m of mediaItems) {
|
|
15841
|
-
const match = m.images?.find((i) => i.language
|
|
15884
|
+
const match = m.images?.find((i) => langMatches(i.language) && i.originalUrl);
|
|
15842
15885
|
if (match?.originalUrl) return match.originalUrl;
|
|
15843
15886
|
}
|
|
15844
15887
|
for (const m of mediaItems) {
|
|
@@ -16166,6 +16209,7 @@ function ProductInfoPrice(props = {}) {
|
|
|
16166
16209
|
currency: derived.currency,
|
|
16167
16210
|
taxZone: derived.taxZone,
|
|
16168
16211
|
portalMode: derived.portalMode,
|
|
16212
|
+
isAuthenticated: resolved.isAuthenticated,
|
|
16169
16213
|
user: derived.user,
|
|
16170
16214
|
labels: derived.labels,
|
|
16171
16215
|
className: props.className
|
|
@@ -16175,7 +16219,7 @@ function ProductInfoPrice(props = {}) {
|
|
|
16175
16219
|
function ProductInfoStock(props = {}) {
|
|
16176
16220
|
const { product, resolved, derived } = useProductInfoContext();
|
|
16177
16221
|
if (!product.inventory) return null;
|
|
16178
|
-
if (propellerV2CoreUi.isContentHidden(derived.portalMode, resolved.user)) return null;
|
|
16222
|
+
if (propellerV2CoreUi.isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) return null;
|
|
16179
16223
|
const Injected = resolved.stockComponent;
|
|
16180
16224
|
if (Injected) {
|
|
16181
16225
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -16202,7 +16246,7 @@ function ProductInfoStock(props = {}) {
|
|
|
16202
16246
|
}
|
|
16203
16247
|
function ProductInfoAddToCart(props = {}) {
|
|
16204
16248
|
const { product, resolved, derived } = useProductInfoContext();
|
|
16205
|
-
if (propellerV2CoreUi.isContentHidden(derived.portalMode, resolved.user)) {
|
|
16249
|
+
if (propellerV2CoreUi.isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) {
|
|
16206
16250
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16207
16251
|
LoginToOrderButton_default,
|
|
16208
16252
|
{
|
|
@@ -16214,7 +16258,18 @@ function ProductInfoAddToCart(props = {}) {
|
|
|
16214
16258
|
}
|
|
16215
16259
|
const Injected = resolved.addToCartComponent;
|
|
16216
16260
|
if (Injected) {
|
|
16217
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16261
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16262
|
+
Injected,
|
|
16263
|
+
{
|
|
16264
|
+
product,
|
|
16265
|
+
cartId: resolved.cartId,
|
|
16266
|
+
createCart: resolved.createCart,
|
|
16267
|
+
onCartCreated: resolved.onCartCreated,
|
|
16268
|
+
includeTax: derived.useTax,
|
|
16269
|
+
labels: derived.labels,
|
|
16270
|
+
className: props.className
|
|
16271
|
+
}
|
|
16272
|
+
);
|
|
16218
16273
|
}
|
|
16219
16274
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16220
16275
|
AddToCart_default,
|
|
@@ -16223,6 +16278,9 @@ function ProductInfoAddToCart(props = {}) {
|
|
|
16223
16278
|
user: derived.user,
|
|
16224
16279
|
currency: derived.currency,
|
|
16225
16280
|
product,
|
|
16281
|
+
cartId: resolved.cartId,
|
|
16282
|
+
createCart: resolved.createCart,
|
|
16283
|
+
onCartCreated: resolved.onCartCreated,
|
|
16226
16284
|
configuration: resolved.configuration,
|
|
16227
16285
|
companyId: resolved.companyId,
|
|
16228
16286
|
includeTax: derived.useTax,
|
|
@@ -16276,6 +16334,7 @@ function ProductInfoBulkPrices(props = {}) {
|
|
|
16276
16334
|
bulkPrices: product.bulkPrices,
|
|
16277
16335
|
includeTax: derived.useTax,
|
|
16278
16336
|
portalMode: derived.portalMode,
|
|
16337
|
+
isAuthenticated: resolved.isAuthenticated,
|
|
16279
16338
|
user: derived.user,
|
|
16280
16339
|
taxZone: derived.taxZone,
|
|
16281
16340
|
currency: derived.currency,
|
|
@@ -16438,7 +16497,7 @@ function ProductSlider(rawProps) {
|
|
|
16438
16497
|
}
|
|
16439
16498
|
function showAddToCart() {
|
|
16440
16499
|
const allow = props.allowAddToCart !== false;
|
|
16441
|
-
return !propellerV2CoreUi.isContentHidden(props.portalMode, props.user) && allow;
|
|
16500
|
+
return !propellerV2CoreUi.isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
|
|
16442
16501
|
}
|
|
16443
16502
|
function isCluster(item) {
|
|
16444
16503
|
return "clusterId" in item && !("productId" in item);
|
|
@@ -16742,14 +16801,10 @@ function ProductVideos(props) {
|
|
|
16742
16801
|
const hasItems = items.length > 0;
|
|
16743
16802
|
const lang = props.language || "NL";
|
|
16744
16803
|
function getVideoUri(video) {
|
|
16745
|
-
|
|
16746
|
-
const match = vids.find((v) => v.language === lang);
|
|
16747
|
-
return match?.uri || vids?.[0]?.uri || "";
|
|
16804
|
+
return propellerV2CoreUi.getLanguageUri(video.videos, lang, "");
|
|
16748
16805
|
}
|
|
16749
16806
|
function getVideoTitle(video) {
|
|
16750
|
-
|
|
16751
|
-
const match = alts.find((a) => a.language === lang);
|
|
16752
|
-
return match?.value || alts?.[0]?.value || "Video";
|
|
16807
|
+
return propellerV2CoreUi.getLanguageString(video.alt, lang, "Video");
|
|
16753
16808
|
}
|
|
16754
16809
|
function isEmbeddable(uri) {
|
|
16755
16810
|
return uri.includes("youtube.com") || uri.includes("youtu.be") || uri.includes("vimeo.com");
|