@propeller-commerce/propeller-v2-react-ui 0.21.0 → 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 +41 -0
- package/dist/index.cjs +86 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -9
- package/dist/index.d.ts +54 -9
- package/dist/index.js +88 -39
- 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/dist/index.d.cts
CHANGED
|
@@ -40,6 +40,14 @@ interface PropellerDeps {
|
|
|
40
40
|
interface PropellerScope {
|
|
41
41
|
/** The authenticated user, or `null` when browsing anonymously. */
|
|
42
42
|
user: Contact | Customer | null;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a session exists, independent of whether `user` has loaded yet.
|
|
45
|
+
* Hosts paint from a cached hint before the profile arrives, so `user` is
|
|
46
|
+
* null for an authenticated visitor for the first frames; without this,
|
|
47
|
+
* semi-closed surfaces flash their logged-out state. Optional — omitting it
|
|
48
|
+
* keeps the previous behaviour.
|
|
49
|
+
*/
|
|
50
|
+
isAuthenticated?: boolean;
|
|
43
51
|
/** Active company ID for the current session; `undefined` for non-company users. */
|
|
44
52
|
companyId: number | undefined;
|
|
45
53
|
/** Active language code (e.g. `'NL'`). */
|
|
@@ -588,7 +596,11 @@ interface UseCartReturn {
|
|
|
588
596
|
loading: boolean;
|
|
589
597
|
/** Last error message, or `null`. */
|
|
590
598
|
error: string | null;
|
|
591
|
-
/**
|
|
599
|
+
/**
|
|
600
|
+
* `false` when a B2B purchaser's authorization limit is exceeded by the cart
|
|
601
|
+
* total, and while a seeded `cartId` is still being hydrated — it fails
|
|
602
|
+
* closed, so gating a checkout button on it holds rather than opens.
|
|
603
|
+
*/
|
|
592
604
|
checkoutAllowed: boolean;
|
|
593
605
|
/** Resolves an existing cart or creates one via the shared `initCart` flow. */
|
|
594
606
|
resolveCart: () => Promise<Cart>;
|
|
@@ -2181,8 +2193,13 @@ interface ResolveSpecEntry {
|
|
|
2181
2193
|
*/
|
|
2182
2194
|
transform?: (gridValue: NonNullable<ProductGridConfig[GridKey]>) => unknown;
|
|
2183
2195
|
}
|
|
2184
|
-
/**
|
|
2185
|
-
|
|
2196
|
+
/**
|
|
2197
|
+
* Per-prop resolution spec: maps each prop key to its {@link ResolveSpecEntry}.
|
|
2198
|
+
*
|
|
2199
|
+
* Infra keys are spec'able too, so a component can pull a provider value it
|
|
2200
|
+
* only reads internally without having to publish it as its own prop.
|
|
2201
|
+
*/
|
|
2202
|
+
type ResolveSpec<P> = Partial<Record<keyof P | keyof PropellerInfra, ResolveSpecEntry>>;
|
|
2186
2203
|
/**
|
|
2187
2204
|
* useResolvedProps — resolves props through the two-tier precedence
|
|
2188
2205
|
* `explicit prop > ProductGrid context > Propeller infra > default`.
|
|
@@ -2191,7 +2208,7 @@ type ResolveSpec<P> = Partial<Record<keyof P, ResolveSpecEntry>>;
|
|
|
2191
2208
|
* @param spec - per-key resolution rules; keys absent from the spec pass through unchanged.
|
|
2192
2209
|
* @returns the props with each spec'd key resolved to its highest-precedence value.
|
|
2193
2210
|
*/
|
|
2194
|
-
declare function useResolvedProps<P extends object>(rawProps: P, spec: ResolveSpec<P>): P
|
|
2211
|
+
declare function useResolvedProps<P extends object>(rawProps: P, spec: ResolveSpec<P>): P & Partial<PropellerInfra>;
|
|
2195
2212
|
|
|
2196
2213
|
/**
|
|
2197
2214
|
* Read the SDK services bundle from `<PropellerDepsProvider>`.
|
|
@@ -3496,13 +3513,17 @@ interface CartOverviewProps {
|
|
|
3496
3513
|
code: string;
|
|
3497
3514
|
name: string;
|
|
3498
3515
|
}[];
|
|
3516
|
+
/** Logged-in user — used for the purchase-authorization check. Resolved from PropellerProvider when omitted. */
|
|
3517
|
+
user?: Contact | Customer | null;
|
|
3518
|
+
/** Active company ID — used for the purchase-authorization check. Resolved from PropellerProvider when omitted. */
|
|
3519
|
+
companyId?: number;
|
|
3499
3520
|
}
|
|
3500
3521
|
/**
|
|
3501
3522
|
* Final cart review panel: shows invoice/delivery addresses, payment, carrier
|
|
3502
3523
|
* and delivery date, with optional reference, notes, terms acceptance and a
|
|
3503
3524
|
* place-order button.
|
|
3504
3525
|
*/
|
|
3505
|
-
declare function CartOverview(
|
|
3526
|
+
declare function CartOverview(rawProps: CartOverviewProps): React$1.JSX.Element;
|
|
3506
3527
|
|
|
3507
3528
|
/**
|
|
3508
3529
|
* @rsc-blocked — Client-only component: interactive state (useState/useReducer).
|
|
@@ -4195,10 +4216,11 @@ declare function ProductCardPrice(props: {
|
|
|
4195
4216
|
/** Renders the embedded `AddToCart` control with all pass-through props from
|
|
4196
4217
|
* context, or the injected `addToCartComponent` when set.
|
|
4197
4218
|
*
|
|
4198
|
-
* Note: the default `AddToCartImpl` accepts a richer prop set
|
|
4199
|
-
*
|
|
4200
|
-
*
|
|
4201
|
-
*
|
|
4219
|
+
* Note: the default `AddToCartImpl` accepts a richer prop set than the public
|
|
4220
|
+
* `AddToCartComponentProps` contract. Injected components receive the whole
|
|
4221
|
+
* contract surface; what stays default-only is infra (graphqlClient, user,
|
|
4222
|
+
* configuration, …), which an injected component resolves from
|
|
4223
|
+
* `<PropellerProvider>` itself. */
|
|
4202
4224
|
declare function ProductCardAddToCart(props: {
|
|
4203
4225
|
/** Override the AddToCart wrapper class. */
|
|
4204
4226
|
className?: string;
|
|
@@ -4549,6 +4571,8 @@ interface CompanySwitcherProps {
|
|
|
4549
4571
|
/** Translated labels keyed by the slugs used inside the component (see
|
|
4550
4572
|
* `getLabel` calls). Missing keys fall back to the English defaults. */
|
|
4551
4573
|
labels?: Record<string, string>;
|
|
4574
|
+
/** Additional class name for the switcher's trigger button. */
|
|
4575
|
+
triggerClassName?: string;
|
|
4552
4576
|
}
|
|
4553
4577
|
/**
|
|
4554
4578
|
* Dropdown for switching the active company of a multi-company contact.
|
|
@@ -6155,6 +6179,12 @@ interface ProductBulkPricesProps {
|
|
|
6155
6179
|
* Defaults to 'open'.
|
|
6156
6180
|
*/
|
|
6157
6181
|
portalMode?: string;
|
|
6182
|
+
/**
|
|
6183
|
+
* Whether a session exists, independent of whether `user` has loaded yet.
|
|
6184
|
+
* Passed down by the parent alongside `portalMode`; closes the hydration
|
|
6185
|
+
* window in which an authenticated visitor still has a null `user`.
|
|
6186
|
+
*/
|
|
6187
|
+
isAuthenticated?: boolean;
|
|
6158
6188
|
/** Authenticated user — used for semi-closed visibility. */
|
|
6159
6189
|
user?: Contact | Customer | null;
|
|
6160
6190
|
/** Tax zone code. Defaults to 'NL'. */
|
|
@@ -6694,6 +6724,15 @@ interface ProductInfoProps {
|
|
|
6694
6724
|
* Example: ['brand', 'color']
|
|
6695
6725
|
*/
|
|
6696
6726
|
textLabels?: string[];
|
|
6727
|
+
/** Cart to add into. Omit and pass `createCart` to start one on first add. */
|
|
6728
|
+
cartId?: string;
|
|
6729
|
+
/** If true a new cart is created when no `cartId` is available. */
|
|
6730
|
+
createCart?: boolean;
|
|
6731
|
+
/**
|
|
6732
|
+
* Called when a new cart is created, so the host can persist `cart.cartId`.
|
|
6733
|
+
* WARNING: without it a new cart is created on every add.
|
|
6734
|
+
*/
|
|
6735
|
+
onCartCreated?: (cart: Cart) => void;
|
|
6697
6736
|
showImage?: boolean;
|
|
6698
6737
|
showBadges?: boolean;
|
|
6699
6738
|
showFavorite?: boolean;
|
|
@@ -6837,6 +6876,12 @@ interface ProductPriceProps {
|
|
|
6837
6876
|
* Defaults to 'open'.
|
|
6838
6877
|
*/
|
|
6839
6878
|
portalMode?: string;
|
|
6879
|
+
/**
|
|
6880
|
+
* Whether a session exists, independent of whether `user` has loaded yet.
|
|
6881
|
+
* Passed down by the parent alongside `portalMode`; closes the hydration
|
|
6882
|
+
* window in which an authenticated visitor still has a null `user`.
|
|
6883
|
+
*/
|
|
6884
|
+
isAuthenticated?: boolean;
|
|
6840
6885
|
/** Authenticated user — used for semi-closed visibility. */
|
|
6841
6886
|
user?: Contact | Customer | null;
|
|
6842
6887
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -40,6 +40,14 @@ interface PropellerDeps {
|
|
|
40
40
|
interface PropellerScope {
|
|
41
41
|
/** The authenticated user, or `null` when browsing anonymously. */
|
|
42
42
|
user: Contact | Customer | null;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a session exists, independent of whether `user` has loaded yet.
|
|
45
|
+
* Hosts paint from a cached hint before the profile arrives, so `user` is
|
|
46
|
+
* null for an authenticated visitor for the first frames; without this,
|
|
47
|
+
* semi-closed surfaces flash their logged-out state. Optional — omitting it
|
|
48
|
+
* keeps the previous behaviour.
|
|
49
|
+
*/
|
|
50
|
+
isAuthenticated?: boolean;
|
|
43
51
|
/** Active company ID for the current session; `undefined` for non-company users. */
|
|
44
52
|
companyId: number | undefined;
|
|
45
53
|
/** Active language code (e.g. `'NL'`). */
|
|
@@ -588,7 +596,11 @@ interface UseCartReturn {
|
|
|
588
596
|
loading: boolean;
|
|
589
597
|
/** Last error message, or `null`. */
|
|
590
598
|
error: string | null;
|
|
591
|
-
/**
|
|
599
|
+
/**
|
|
600
|
+
* `false` when a B2B purchaser's authorization limit is exceeded by the cart
|
|
601
|
+
* total, and while a seeded `cartId` is still being hydrated — it fails
|
|
602
|
+
* closed, so gating a checkout button on it holds rather than opens.
|
|
603
|
+
*/
|
|
592
604
|
checkoutAllowed: boolean;
|
|
593
605
|
/** Resolves an existing cart or creates one via the shared `initCart` flow. */
|
|
594
606
|
resolveCart: () => Promise<Cart>;
|
|
@@ -2181,8 +2193,13 @@ interface ResolveSpecEntry {
|
|
|
2181
2193
|
*/
|
|
2182
2194
|
transform?: (gridValue: NonNullable<ProductGridConfig[GridKey]>) => unknown;
|
|
2183
2195
|
}
|
|
2184
|
-
/**
|
|
2185
|
-
|
|
2196
|
+
/**
|
|
2197
|
+
* Per-prop resolution spec: maps each prop key to its {@link ResolveSpecEntry}.
|
|
2198
|
+
*
|
|
2199
|
+
* Infra keys are spec'able too, so a component can pull a provider value it
|
|
2200
|
+
* only reads internally without having to publish it as its own prop.
|
|
2201
|
+
*/
|
|
2202
|
+
type ResolveSpec<P> = Partial<Record<keyof P | keyof PropellerInfra, ResolveSpecEntry>>;
|
|
2186
2203
|
/**
|
|
2187
2204
|
* useResolvedProps — resolves props through the two-tier precedence
|
|
2188
2205
|
* `explicit prop > ProductGrid context > Propeller infra > default`.
|
|
@@ -2191,7 +2208,7 @@ type ResolveSpec<P> = Partial<Record<keyof P, ResolveSpecEntry>>;
|
|
|
2191
2208
|
* @param spec - per-key resolution rules; keys absent from the spec pass through unchanged.
|
|
2192
2209
|
* @returns the props with each spec'd key resolved to its highest-precedence value.
|
|
2193
2210
|
*/
|
|
2194
|
-
declare function useResolvedProps<P extends object>(rawProps: P, spec: ResolveSpec<P>): P
|
|
2211
|
+
declare function useResolvedProps<P extends object>(rawProps: P, spec: ResolveSpec<P>): P & Partial<PropellerInfra>;
|
|
2195
2212
|
|
|
2196
2213
|
/**
|
|
2197
2214
|
* Read the SDK services bundle from `<PropellerDepsProvider>`.
|
|
@@ -3496,13 +3513,17 @@ interface CartOverviewProps {
|
|
|
3496
3513
|
code: string;
|
|
3497
3514
|
name: string;
|
|
3498
3515
|
}[];
|
|
3516
|
+
/** Logged-in user — used for the purchase-authorization check. Resolved from PropellerProvider when omitted. */
|
|
3517
|
+
user?: Contact | Customer | null;
|
|
3518
|
+
/** Active company ID — used for the purchase-authorization check. Resolved from PropellerProvider when omitted. */
|
|
3519
|
+
companyId?: number;
|
|
3499
3520
|
}
|
|
3500
3521
|
/**
|
|
3501
3522
|
* Final cart review panel: shows invoice/delivery addresses, payment, carrier
|
|
3502
3523
|
* and delivery date, with optional reference, notes, terms acceptance and a
|
|
3503
3524
|
* place-order button.
|
|
3504
3525
|
*/
|
|
3505
|
-
declare function CartOverview(
|
|
3526
|
+
declare function CartOverview(rawProps: CartOverviewProps): React$1.JSX.Element;
|
|
3506
3527
|
|
|
3507
3528
|
/**
|
|
3508
3529
|
* @rsc-blocked — Client-only component: interactive state (useState/useReducer).
|
|
@@ -4195,10 +4216,11 @@ declare function ProductCardPrice(props: {
|
|
|
4195
4216
|
/** Renders the embedded `AddToCart` control with all pass-through props from
|
|
4196
4217
|
* context, or the injected `addToCartComponent` when set.
|
|
4197
4218
|
*
|
|
4198
|
-
* Note: the default `AddToCartImpl` accepts a richer prop set
|
|
4199
|
-
*
|
|
4200
|
-
*
|
|
4201
|
-
*
|
|
4219
|
+
* Note: the default `AddToCartImpl` accepts a richer prop set than the public
|
|
4220
|
+
* `AddToCartComponentProps` contract. Injected components receive the whole
|
|
4221
|
+
* contract surface; what stays default-only is infra (graphqlClient, user,
|
|
4222
|
+
* configuration, …), which an injected component resolves from
|
|
4223
|
+
* `<PropellerProvider>` itself. */
|
|
4202
4224
|
declare function ProductCardAddToCart(props: {
|
|
4203
4225
|
/** Override the AddToCart wrapper class. */
|
|
4204
4226
|
className?: string;
|
|
@@ -4549,6 +4571,8 @@ interface CompanySwitcherProps {
|
|
|
4549
4571
|
/** Translated labels keyed by the slugs used inside the component (see
|
|
4550
4572
|
* `getLabel` calls). Missing keys fall back to the English defaults. */
|
|
4551
4573
|
labels?: Record<string, string>;
|
|
4574
|
+
/** Additional class name for the switcher's trigger button. */
|
|
4575
|
+
triggerClassName?: string;
|
|
4552
4576
|
}
|
|
4553
4577
|
/**
|
|
4554
4578
|
* Dropdown for switching the active company of a multi-company contact.
|
|
@@ -6155,6 +6179,12 @@ interface ProductBulkPricesProps {
|
|
|
6155
6179
|
* Defaults to 'open'.
|
|
6156
6180
|
*/
|
|
6157
6181
|
portalMode?: string;
|
|
6182
|
+
/**
|
|
6183
|
+
* Whether a session exists, independent of whether `user` has loaded yet.
|
|
6184
|
+
* Passed down by the parent alongside `portalMode`; closes the hydration
|
|
6185
|
+
* window in which an authenticated visitor still has a null `user`.
|
|
6186
|
+
*/
|
|
6187
|
+
isAuthenticated?: boolean;
|
|
6158
6188
|
/** Authenticated user — used for semi-closed visibility. */
|
|
6159
6189
|
user?: Contact | Customer | null;
|
|
6160
6190
|
/** Tax zone code. Defaults to 'NL'. */
|
|
@@ -6694,6 +6724,15 @@ interface ProductInfoProps {
|
|
|
6694
6724
|
* Example: ['brand', 'color']
|
|
6695
6725
|
*/
|
|
6696
6726
|
textLabels?: string[];
|
|
6727
|
+
/** Cart to add into. Omit and pass `createCart` to start one on first add. */
|
|
6728
|
+
cartId?: string;
|
|
6729
|
+
/** If true a new cart is created when no `cartId` is available. */
|
|
6730
|
+
createCart?: boolean;
|
|
6731
|
+
/**
|
|
6732
|
+
* Called when a new cart is created, so the host can persist `cart.cartId`.
|
|
6733
|
+
* WARNING: without it a new cart is created on every add.
|
|
6734
|
+
*/
|
|
6735
|
+
onCartCreated?: (cart: Cart) => void;
|
|
6697
6736
|
showImage?: boolean;
|
|
6698
6737
|
showBadges?: boolean;
|
|
6699
6738
|
showFavorite?: boolean;
|
|
@@ -6837,6 +6876,12 @@ interface ProductPriceProps {
|
|
|
6837
6876
|
* Defaults to 'open'.
|
|
6838
6877
|
*/
|
|
6839
6878
|
portalMode?: string;
|
|
6879
|
+
/**
|
|
6880
|
+
* Whether a session exists, independent of whether `user` has loaded yet.
|
|
6881
|
+
* Passed down by the parent alongside `portalMode`; closes the hydration
|
|
6882
|
+
* window in which an authenticated visitor still has a null `user`.
|
|
6883
|
+
*/
|
|
6884
|
+
isAuthenticated?: boolean;
|
|
6840
6885
|
/** Authenticated user — used for semi-closed visibility. */
|
|
6841
6886
|
user?: Contact | Customer | null;
|
|
6842
6887
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { getLabel, getLocalizedValue, formatPrice, localeForLanguage, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl,
|
|
2
|
+
import { getLabel, getLocalizedValue, formatPrice, localeForLanguage, getClusterSku, getClusterImageUrl, isContentHidden, getProductSku, getProductImageUrl, createServices, isCheckoutAllowed, err, ok, formatSurcharge, deriveUserMode, isContact, isCustomer, getAddresses, attributeNameMatches, getAttributeDisplayName, filterProductsBySelections, extractAttributeValues, buildInventoryFilter, findPurchaserPac, isOverAuthorizationLimit, MIN_STOCK_THRESHOLD, getNettedBonusItems, getLanguageString, collectAttributeValues, getCountryName, resolveLanguageEntry, getLanguageUri } from '@propeller-commerce/propeller-v2-core-ui';
|
|
3
3
|
export * from '@propeller-commerce/propeller-v2-core-ui';
|
|
4
4
|
import * as React6 from 'react';
|
|
5
|
-
import { createContext, useState, useRef, useCallback, useMemo, useContext
|
|
5
|
+
import { createContext, useState, useRef, useCallback, useEffect, useMemo, useContext } from 'react';
|
|
6
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
import { ProductStatus, ProductSortField, SortOrder, CrossupsellType, CartStatus, CartAddressType, Gender, YesNo, AddressType, MagicTokenService, PaymentStatuses, OrderSearchFields, OrderItemClass, ProductSearchableField, machineService, PurchaseRole, AttributeType, OrderSortField, OrderType, BundleCondition, OrderDiscountType } from '@propeller-commerce/propeller-sdk-v2';
|
|
8
8
|
import { clsx } from 'clsx';
|
|
@@ -747,10 +747,29 @@ function useCart(options) {
|
|
|
747
747
|
const [loading, setLoading] = useState(false);
|
|
748
748
|
const [error, setError] = useState(null);
|
|
749
749
|
const notesTimers = useRef({});
|
|
750
|
-
|
|
751
|
-
(
|
|
752
|
-
|
|
753
|
-
|
|
750
|
+
useEffect(() => {
|
|
751
|
+
if (!graphqlClient || !cartId || cart) return;
|
|
752
|
+
let cancelled = false;
|
|
753
|
+
(async () => {
|
|
754
|
+
try {
|
|
755
|
+
const fetched = await createServices(graphqlClient).cart.getCart({
|
|
756
|
+
cartId,
|
|
757
|
+
imageSearchFilters: imageSearchFilters(),
|
|
758
|
+
imageVariantFilters: imageVariantFilters(),
|
|
759
|
+
language
|
|
760
|
+
});
|
|
761
|
+
if (!cancelled && fetched) setCart(fetched);
|
|
762
|
+
} catch {
|
|
763
|
+
}
|
|
764
|
+
})();
|
|
765
|
+
return () => {
|
|
766
|
+
cancelled = true;
|
|
767
|
+
};
|
|
768
|
+
}, [graphqlClient, cartId, cart, language, imageSearchFilters, imageVariantFilters]);
|
|
769
|
+
const checkoutAllowed = useMemo(() => {
|
|
770
|
+
if (cartId && !cart) return false;
|
|
771
|
+
return isCheckoutAllowed(user, resolvedCompanyId, cart);
|
|
772
|
+
}, [user, resolvedCompanyId, cart, cartId]);
|
|
754
773
|
function getMinQuantity(product) {
|
|
755
774
|
const min = product?.minimumQuantity;
|
|
756
775
|
return min && min > 0 ? min : 1;
|
|
@@ -1571,7 +1590,8 @@ var INFRA_KEYS = [
|
|
|
1571
1590
|
"includeTax",
|
|
1572
1591
|
"currency",
|
|
1573
1592
|
"configuration",
|
|
1574
|
-
"portalMode"
|
|
1593
|
+
"portalMode",
|
|
1594
|
+
"isAuthenticated"
|
|
1575
1595
|
];
|
|
1576
1596
|
function useInfraProps(props) {
|
|
1577
1597
|
const ctx = usePropellerContext();
|
|
@@ -2248,10 +2268,11 @@ function useProductSearch(options) {
|
|
|
2248
2268
|
const isLoading = !isControlled && internalLoading;
|
|
2249
2269
|
function filterByLanguage(products, lang) {
|
|
2250
2270
|
if (!lang) return products;
|
|
2271
|
+
const target = lang.toUpperCase();
|
|
2251
2272
|
return products.filter((p) => {
|
|
2252
2273
|
const names = p.names || p.names || [];
|
|
2253
2274
|
if (!names || names.length === 0) return true;
|
|
2254
|
-
return names.some((n) => n.language ===
|
|
2275
|
+
return names.some((n) => (n.language || "").toUpperCase() === target);
|
|
2255
2276
|
});
|
|
2256
2277
|
}
|
|
2257
2278
|
const fetchProducts = useCallback(async () => {
|
|
@@ -4208,7 +4229,7 @@ function AccountIconAndMenu(rawProps) {
|
|
|
4208
4229
|
onClick: (event) => handleIconClick(),
|
|
4209
4230
|
"aria-label": getLabel(props.labels, "accountLabel", "Account"),
|
|
4210
4231
|
"data-open": menuOpen ? "true" : "false",
|
|
4211
|
-
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-
|
|
4232
|
+
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 : ""}`),
|
|
4212
4233
|
children: [
|
|
4213
4234
|
/* @__PURE__ */ jsx(
|
|
4214
4235
|
"svg",
|
|
@@ -7620,7 +7641,8 @@ function CartIconAndSidebar(rawProps) {
|
|
|
7620
7641
|
] });
|
|
7621
7642
|
}
|
|
7622
7643
|
var CartIconAndSidebar_default = CartIconAndSidebar;
|
|
7623
|
-
function CartOverview(
|
|
7644
|
+
function CartOverview(rawProps) {
|
|
7645
|
+
const props = useInfraProps(rawProps);
|
|
7624
7646
|
const [reference, setReference] = useState("");
|
|
7625
7647
|
const [notes, setNotes] = useState("");
|
|
7626
7648
|
const [termsAccepted, setTermsAccepted] = useState(false);
|
|
@@ -7630,6 +7652,7 @@ function CartOverview(props) {
|
|
|
7630
7652
|
const showReference = props.showReference !== false;
|
|
7631
7653
|
const showTermsAndConditions = props.showTermsAndConditions !== false;
|
|
7632
7654
|
const showPurchaseButton = props.showPurchaseButton !== false;
|
|
7655
|
+
const overLimit = isOverAuthorizationLimit(props.user, props.companyId, props.cart);
|
|
7633
7656
|
const invoiceAddress = props.cart?.invoiceAddress;
|
|
7634
7657
|
const deliveryAddress = props.cart?.deliveryAddress;
|
|
7635
7658
|
const getCountryName$1 = (code) => getCountryName(code, props.countries);
|
|
@@ -7769,7 +7792,12 @@ function CartOverview(props) {
|
|
|
7769
7792
|
] });
|
|
7770
7793
|
})() })
|
|
7771
7794
|
] }) : null,
|
|
7772
|
-
showPurchaseButton ? /* @__PURE__ */
|
|
7795
|
+
showPurchaseButton && overLimit ? /* @__PURE__ */ jsx("p", { className: "propeller-cart-overview__authorization-required text-sm text-muted-foreground mt-2", children: getLabel(
|
|
7796
|
+
props.labels,
|
|
7797
|
+
"authorizationRequired",
|
|
7798
|
+
"This order exceeds your authorization limit. Request authorization from the cart to continue."
|
|
7799
|
+
) }) : null,
|
|
7800
|
+
showPurchaseButton && !overLimit ? /* @__PURE__ */ jsxs(
|
|
7773
7801
|
"button",
|
|
7774
7802
|
{
|
|
7775
7803
|
type: "button",
|
|
@@ -8144,6 +8172,7 @@ var RESOLVE_SPEC = {
|
|
|
8144
8172
|
currency: { infra: "currency", default: "\u20AC" },
|
|
8145
8173
|
user: { infra: "user", default: null },
|
|
8146
8174
|
portalMode: { infra: "portalMode" },
|
|
8175
|
+
isAuthenticated: { infra: "isAuthenticated" },
|
|
8147
8176
|
columns: { grid: "columns", default: 3 },
|
|
8148
8177
|
showPrice: { grid: "showPrice" },
|
|
8149
8178
|
showStock: { grid: "showStock" },
|
|
@@ -8187,7 +8216,7 @@ function ClusterCard(rawProps) {
|
|
|
8187
8216
|
const textLabelValues = resolveAttributeValues(cluster, props.textLabels).map((value) => ({
|
|
8188
8217
|
value
|
|
8189
8218
|
}));
|
|
8190
|
-
const contentHidden = isContentHidden(props.portalMode, props.user);
|
|
8219
|
+
const contentHidden = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
8191
8220
|
const defaultProductInventory = contentHidden ? void 0 : cluster?.defaultProduct?.inventory;
|
|
8192
8221
|
const useTax = props.includeTax !== void 0 ? !!props.includeTax : false;
|
|
8193
8222
|
const priceObj = contentHidden ? void 0 : cluster?.defaultProduct?.price;
|
|
@@ -8614,7 +8643,7 @@ function LoginToOrderButton(props) {
|
|
|
8614
8643
|
var LoginToOrderButton_default = LoginToOrderButton;
|
|
8615
8644
|
function ProductPriceDisplay(props) {
|
|
8616
8645
|
function isHidden2() {
|
|
8617
|
-
return isContentHidden(props.portalMode, props.user);
|
|
8646
|
+
return isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
8618
8647
|
}
|
|
8619
8648
|
function formatPrice17(value) {
|
|
8620
8649
|
if (value === null || value === void 0) return "";
|
|
@@ -8706,6 +8735,7 @@ var RESOLVE_SPEC2 = {
|
|
|
8706
8735
|
language: { infra: "language", default: "NL" },
|
|
8707
8736
|
includeTax: { infra: "includeTax" },
|
|
8708
8737
|
portalMode: { infra: "portalMode" },
|
|
8738
|
+
isAuthenticated: { infra: "isAuthenticated" },
|
|
8709
8739
|
configuration: { infra: "configuration" },
|
|
8710
8740
|
// Same entry ClusterCard has always had. Without it a shop priced in
|
|
8711
8741
|
// anything but euros had no route to the cards at all: the grid resolved the
|
|
@@ -8764,7 +8794,7 @@ function ProductCardRoot(rawProps) {
|
|
|
8764
8794
|
const language = props.language || "NL";
|
|
8765
8795
|
const product = props.product;
|
|
8766
8796
|
const isRow = props.columns === 1;
|
|
8767
|
-
const contentHidden = isContentHidden(props.portalMode, props.user);
|
|
8797
|
+
const contentHidden = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
8768
8798
|
const showStock = !!props.showStock && !contentHidden;
|
|
8769
8799
|
const showCta = props.allowAddToCart !== false || contentHidden;
|
|
8770
8800
|
const name = getProductName(product, language);
|
|
@@ -9061,6 +9091,7 @@ function ProductCardPrice(props) {
|
|
|
9061
9091
|
currency: resolved.currency,
|
|
9062
9092
|
language: resolved.language,
|
|
9063
9093
|
portalMode: resolved.portalMode,
|
|
9094
|
+
isAuthenticated: resolved.isAuthenticated,
|
|
9064
9095
|
user: resolved.user,
|
|
9065
9096
|
showLoginPrompt: false
|
|
9066
9097
|
}
|
|
@@ -9068,7 +9099,7 @@ function ProductCardPrice(props) {
|
|
|
9068
9099
|
}
|
|
9069
9100
|
function ProductCardAddToCart(props) {
|
|
9070
9101
|
const { product, resolved } = useProductCard();
|
|
9071
|
-
if (isContentHidden(resolved.portalMode, resolved.user)) {
|
|
9102
|
+
if (isContentHidden(resolved.portalMode, resolved.user, resolved.isAuthenticated)) {
|
|
9072
9103
|
return /* @__PURE__ */ jsx(
|
|
9073
9104
|
LoginToOrderButton_default,
|
|
9074
9105
|
{
|
|
@@ -9089,9 +9120,13 @@ function ProductCardAddToCart(props) {
|
|
|
9089
9120
|
allowIncrDecr: resolved.allowIncrDecr,
|
|
9090
9121
|
showModal: resolved.showModal,
|
|
9091
9122
|
enableStockValidation: resolved.enableStockValidation,
|
|
9123
|
+
onAddToCart: resolved.onAddToCart,
|
|
9092
9124
|
afterAddToCart: resolved.afterAddToCart,
|
|
9093
9125
|
onProceedToCheckout: resolved.onProceedToCheckout,
|
|
9094
9126
|
onRequestQuoteClick: resolved.onRequestQuoteClick,
|
|
9127
|
+
createCart: resolved.createCart,
|
|
9128
|
+
onCartCreated: resolved.onCartCreated,
|
|
9129
|
+
includeTax: resolved.includeTax,
|
|
9095
9130
|
labels: resolved.addToCartLabels
|
|
9096
9131
|
}
|
|
9097
9132
|
);
|
|
@@ -9214,7 +9249,7 @@ function ProductGrid(rawProps) {
|
|
|
9214
9249
|
}
|
|
9215
9250
|
function showAddToCart() {
|
|
9216
9251
|
const allow = props.allowAddToCart !== false;
|
|
9217
|
-
return !isContentHidden(props.portalMode, props.user) && allow;
|
|
9252
|
+
return !isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
|
|
9218
9253
|
}
|
|
9219
9254
|
function getSkeletonItems() {
|
|
9220
9255
|
const cols = props.columns || 3;
|
|
@@ -9492,7 +9527,7 @@ function GridFilters(rawProps) {
|
|
|
9492
9527
|
}
|
|
9493
9528
|
function showAvailability() {
|
|
9494
9529
|
if (!props.showAvailabilityFilter) return false;
|
|
9495
|
-
return !isContentHidden(props.portalMode, props.user);
|
|
9530
|
+
return !isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
9496
9531
|
}
|
|
9497
9532
|
function getFilterName(filter) {
|
|
9498
9533
|
return filter?.attributeDescription?.name || "";
|
|
@@ -10047,7 +10082,7 @@ function GridToolbar(rawProps) {
|
|
|
10047
10082
|
return badges;
|
|
10048
10083
|
}
|
|
10049
10084
|
function isPriceSortDisabled() {
|
|
10050
|
-
return isContentHidden(props.portalMode, props.user);
|
|
10085
|
+
return isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
10051
10086
|
}
|
|
10052
10087
|
function getAvailabilityLabel() {
|
|
10053
10088
|
const qty = props.minStock;
|
|
@@ -10900,7 +10935,7 @@ function ClusterOptions(rawProps) {
|
|
|
10900
10935
|
const language = props.language;
|
|
10901
10936
|
const options = props.options || [];
|
|
10902
10937
|
const sel = selectedProductIds;
|
|
10903
|
-
const hidePrices = isContentHidden(props.portalMode, props.user);
|
|
10938
|
+
const hidePrices = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
10904
10939
|
return options.filter((option) => option.hidden !== YesNo.Y).map((option) => {
|
|
10905
10940
|
const idStr = option.id.toString();
|
|
10906
10941
|
const selectedProductId = sel[idStr] || "";
|
|
@@ -11084,7 +11119,11 @@ function CompanySwitcher(props) {
|
|
|
11084
11119
|
type: "button",
|
|
11085
11120
|
"aria-haspopup": "listbox",
|
|
11086
11121
|
"aria-label": getLabel(props.labels, "switchCompanyAriaLabel", "Switch company"),
|
|
11087
|
-
className:
|
|
11122
|
+
className: cn(
|
|
11123
|
+
// `text-inherit`: this sits in the host's header, which may be dark or
|
|
11124
|
+
// light. See the note on AccountIconAndMenu's trigger.
|
|
11125
|
+
`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 : ""}`
|
|
11126
|
+
),
|
|
11088
11127
|
onClick: (event) => toggleDropdown(),
|
|
11089
11128
|
"aria-expanded": isOpen,
|
|
11090
11129
|
children: [
|
|
@@ -15059,7 +15098,7 @@ function PriceToggle(props) {
|
|
|
15059
15098
|
var PriceToggle_default = PriceToggle;
|
|
15060
15099
|
function ProductBulkPrices(props) {
|
|
15061
15100
|
const includeTax = !!props.includeTax;
|
|
15062
|
-
const isHidden2 = isContentHidden(props.portalMode, props.user);
|
|
15101
|
+
const isHidden2 = isContentHidden(props.portalMode, props.user, props.isAuthenticated);
|
|
15063
15102
|
function getTierQuantity(tier) {
|
|
15064
15103
|
const discount = tier.discount;
|
|
15065
15104
|
return discount?.quantityFrom ?? tier.quantity ?? null;
|
|
@@ -15606,14 +15645,10 @@ function ProductDownloads(props) {
|
|
|
15606
15645
|
const hasItems = items.length > 0;
|
|
15607
15646
|
const lang = props.language || "NL";
|
|
15608
15647
|
function getDocumentUrl(doc) {
|
|
15609
|
-
|
|
15610
|
-
const match = docs.find((d) => d.language === lang);
|
|
15611
|
-
return match?.originalUrl || docs?.[0]?.originalUrl || "";
|
|
15648
|
+
return resolveLanguageEntry(doc.documents, lang)?.originalUrl || "";
|
|
15612
15649
|
}
|
|
15613
15650
|
function getDocumentName(doc) {
|
|
15614
|
-
|
|
15615
|
-
const match = alts.find((a) => a.language === lang);
|
|
15616
|
-
return match?.value || alts?.[0]?.value || "Download";
|
|
15651
|
+
return getLanguageString(doc.alt, lang, "Download");
|
|
15617
15652
|
}
|
|
15618
15653
|
return /* @__PURE__ */ jsxs("div", { className: cn(`propeller-product-downloads ${props.className || ""}`), children: [
|
|
15619
15654
|
hasItems ? /* @__PURE__ */ jsx("h3", { className: "propeller-product-downloads__title text-base font-semibold text-foreground mb-3", children: getLabel(props.labels, "title", "Downloads") }) : null,
|
|
@@ -15819,12 +15854,14 @@ function ProductGallery(props) {
|
|
|
15819
15854
|
var ProductGallery_default = ProductGallery;
|
|
15820
15855
|
function pickImageUrl(mediaItems, target) {
|
|
15821
15856
|
if (!mediaItems || mediaItems.length === 0) return null;
|
|
15857
|
+
const targetUpper = (target || "").toUpperCase();
|
|
15858
|
+
const langMatches = (value) => (value || "").toUpperCase() === targetUpper;
|
|
15822
15859
|
for (const m of mediaItems) {
|
|
15823
|
-
const match = m.imageVariants?.find((v) => v.language
|
|
15860
|
+
const match = m.imageVariants?.find((v) => langMatches(v.language) && v.url);
|
|
15824
15861
|
if (match?.url) return match.url;
|
|
15825
15862
|
}
|
|
15826
15863
|
for (const m of mediaItems) {
|
|
15827
|
-
const match = m.images?.find((i) => i.language
|
|
15864
|
+
const match = m.images?.find((i) => langMatches(i.language) && i.originalUrl);
|
|
15828
15865
|
if (match?.originalUrl) return match.originalUrl;
|
|
15829
15866
|
}
|
|
15830
15867
|
for (const m of mediaItems) {
|
|
@@ -16152,6 +16189,7 @@ function ProductInfoPrice(props = {}) {
|
|
|
16152
16189
|
currency: derived.currency,
|
|
16153
16190
|
taxZone: derived.taxZone,
|
|
16154
16191
|
portalMode: derived.portalMode,
|
|
16192
|
+
isAuthenticated: resolved.isAuthenticated,
|
|
16155
16193
|
user: derived.user,
|
|
16156
16194
|
labels: derived.labels,
|
|
16157
16195
|
className: props.className
|
|
@@ -16161,7 +16199,7 @@ function ProductInfoPrice(props = {}) {
|
|
|
16161
16199
|
function ProductInfoStock(props = {}) {
|
|
16162
16200
|
const { product, resolved, derived } = useProductInfoContext();
|
|
16163
16201
|
if (!product.inventory) return null;
|
|
16164
|
-
if (isContentHidden(derived.portalMode, resolved.user)) return null;
|
|
16202
|
+
if (isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) return null;
|
|
16165
16203
|
const Injected = resolved.stockComponent;
|
|
16166
16204
|
if (Injected) {
|
|
16167
16205
|
return /* @__PURE__ */ jsx(
|
|
@@ -16188,7 +16226,7 @@ function ProductInfoStock(props = {}) {
|
|
|
16188
16226
|
}
|
|
16189
16227
|
function ProductInfoAddToCart(props = {}) {
|
|
16190
16228
|
const { product, resolved, derived } = useProductInfoContext();
|
|
16191
|
-
if (isContentHidden(derived.portalMode, resolved.user)) {
|
|
16229
|
+
if (isContentHidden(derived.portalMode, resolved.user, resolved.isAuthenticated)) {
|
|
16192
16230
|
return /* @__PURE__ */ jsx(
|
|
16193
16231
|
LoginToOrderButton_default,
|
|
16194
16232
|
{
|
|
@@ -16200,7 +16238,18 @@ function ProductInfoAddToCart(props = {}) {
|
|
|
16200
16238
|
}
|
|
16201
16239
|
const Injected = resolved.addToCartComponent;
|
|
16202
16240
|
if (Injected) {
|
|
16203
|
-
return /* @__PURE__ */ jsx(
|
|
16241
|
+
return /* @__PURE__ */ jsx(
|
|
16242
|
+
Injected,
|
|
16243
|
+
{
|
|
16244
|
+
product,
|
|
16245
|
+
cartId: resolved.cartId,
|
|
16246
|
+
createCart: resolved.createCart,
|
|
16247
|
+
onCartCreated: resolved.onCartCreated,
|
|
16248
|
+
includeTax: derived.useTax,
|
|
16249
|
+
labels: derived.labels,
|
|
16250
|
+
className: props.className
|
|
16251
|
+
}
|
|
16252
|
+
);
|
|
16204
16253
|
}
|
|
16205
16254
|
return /* @__PURE__ */ jsx(
|
|
16206
16255
|
AddToCart_default,
|
|
@@ -16209,6 +16258,9 @@ function ProductInfoAddToCart(props = {}) {
|
|
|
16209
16258
|
user: derived.user,
|
|
16210
16259
|
currency: derived.currency,
|
|
16211
16260
|
product,
|
|
16261
|
+
cartId: resolved.cartId,
|
|
16262
|
+
createCart: resolved.createCart,
|
|
16263
|
+
onCartCreated: resolved.onCartCreated,
|
|
16212
16264
|
configuration: resolved.configuration,
|
|
16213
16265
|
companyId: resolved.companyId,
|
|
16214
16266
|
includeTax: derived.useTax,
|
|
@@ -16262,6 +16314,7 @@ function ProductInfoBulkPrices(props = {}) {
|
|
|
16262
16314
|
bulkPrices: product.bulkPrices,
|
|
16263
16315
|
includeTax: derived.useTax,
|
|
16264
16316
|
portalMode: derived.portalMode,
|
|
16317
|
+
isAuthenticated: resolved.isAuthenticated,
|
|
16265
16318
|
user: derived.user,
|
|
16266
16319
|
taxZone: derived.taxZone,
|
|
16267
16320
|
currency: derived.currency,
|
|
@@ -16424,7 +16477,7 @@ function ProductSlider(rawProps) {
|
|
|
16424
16477
|
}
|
|
16425
16478
|
function showAddToCart() {
|
|
16426
16479
|
const allow = props.allowAddToCart !== false;
|
|
16427
|
-
return !isContentHidden(props.portalMode, props.user) && allow;
|
|
16480
|
+
return !isContentHidden(props.portalMode, props.user, props.isAuthenticated) && allow;
|
|
16428
16481
|
}
|
|
16429
16482
|
function isCluster(item) {
|
|
16430
16483
|
return "clusterId" in item && !("productId" in item);
|
|
@@ -16728,14 +16781,10 @@ function ProductVideos(props) {
|
|
|
16728
16781
|
const hasItems = items.length > 0;
|
|
16729
16782
|
const lang = props.language || "NL";
|
|
16730
16783
|
function getVideoUri(video) {
|
|
16731
|
-
|
|
16732
|
-
const match = vids.find((v) => v.language === lang);
|
|
16733
|
-
return match?.uri || vids?.[0]?.uri || "";
|
|
16784
|
+
return getLanguageUri(video.videos, lang, "");
|
|
16734
16785
|
}
|
|
16735
16786
|
function getVideoTitle(video) {
|
|
16736
|
-
|
|
16737
|
-
const match = alts.find((a) => a.language === lang);
|
|
16738
|
-
return match?.value || alts?.[0]?.value || "Video";
|
|
16787
|
+
return getLanguageString(video.alt, lang, "Video");
|
|
16739
16788
|
}
|
|
16740
16789
|
function isEmbeddable(uri) {
|
|
16741
16790
|
return uri.includes("youtube.com") || uri.includes("youtu.be") || uri.includes("vimeo.com");
|