@cimplify/sdk 0.9.8 → 0.9.9
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/dist/react.d.mts +55 -2
- package/dist/react.d.ts +55 -2
- package/dist/react.js +519 -0
- package/dist/react.mjs +517 -1
- package/package.json +1 -1
package/dist/react.d.mts
CHANGED
|
@@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';
|
|
|
2
2
|
import { A as AdSlot, a as AdPosition, e as AdContextValue } from './ads-MkGm5l1T.mjs';
|
|
3
3
|
export { c as AdConfig, g as getVariantDisplayName } from './ads-MkGm5l1T.mjs';
|
|
4
4
|
import { C as CimplifyClient, Y as ProcessCheckoutResult, _ as CheckoutStatus, $ as CheckoutStatusContext, bq as Location, bn as Business, aS as Order, d as QuoteBundleSelectionInput, Q as QuoteCompositeSelectionInput, P as PriceQuote, g as QuoteUiMessage, w as ElementsOptions, cK as AuthenticatedData, cG as AddressInfo, cH as PaymentMethodInfo, s as CimplifyElements, cM as ElementsCheckoutResult, X as ProcessCheckoutOptions } from './client-D4EiM667.mjs';
|
|
5
|
-
import { s as Product, d as Pagination, h as CimplifyError, t as ProductWithDetails, N as Category, aE as BundleSelectionInput, af as ComponentSelectionInput, Q as Collection, C as CurrencyCode, u as ProductVariant, x as VariantAxisWithValues, M as Money, H as AddOnWithOptions } from './payment-D58dS_E9.mjs';
|
|
5
|
+
import { s as Product, d as Pagination, h as CimplifyError, t as ProductWithDetails, N as Category, aE as BundleSelectionInput, af as ComponentSelectionInput, Q as Collection, C as CurrencyCode, u as ProductVariant, x as VariantAxisWithValues, M as Money, H as AddOnWithOptions, a1 as BundleComponentView, ac as CompositeGroupView, ag as CompositePriceResult } from './payment-D58dS_E9.mjs';
|
|
6
6
|
|
|
7
7
|
interface UserIdentity {
|
|
8
8
|
uid: string;
|
|
@@ -420,6 +420,59 @@ interface AddOnSelectorProps {
|
|
|
420
420
|
*/
|
|
421
421
|
declare function AddOnSelector({ addOns, selectedOptions, onOptionsChange, className, }: AddOnSelectorProps): React.ReactElement | null;
|
|
422
422
|
|
|
423
|
+
interface BundleSelectorProps {
|
|
424
|
+
components: BundleComponentView[];
|
|
425
|
+
bundlePrice?: Money;
|
|
426
|
+
discountValue?: Money;
|
|
427
|
+
onSelectionsChange: (selections: BundleSelectionInput[]) => void;
|
|
428
|
+
onReady?: (ready: boolean) => void;
|
|
429
|
+
className?: string;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* BundleSelector — curated product set with optional variant choices.
|
|
433
|
+
*
|
|
434
|
+
* Each bundle component shows the included product. If `allow_variant_choice`
|
|
435
|
+
* is true, the customer can pick which variant they want for that slot.
|
|
436
|
+
*
|
|
437
|
+
* Accepts pre-fetched `BundleComponentView[]` from the product detail response
|
|
438
|
+
* — no internal data fetching.
|
|
439
|
+
*/
|
|
440
|
+
declare function BundleSelector({ components, bundlePrice, discountValue, onSelectionsChange, onReady, className, }: BundleSelectorProps): React.ReactElement | null;
|
|
441
|
+
|
|
442
|
+
interface CompositeSelectorProps {
|
|
443
|
+
compositeId: string;
|
|
444
|
+
groups: CompositeGroupView[];
|
|
445
|
+
onSelectionsChange: (selections: ComponentSelectionInput[]) => void;
|
|
446
|
+
onPriceChange?: (price: CompositePriceResult | null) => void;
|
|
447
|
+
onReady?: (ready: boolean) => void;
|
|
448
|
+
className?: string;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* CompositeSelector — "build your own" product configurator.
|
|
452
|
+
*
|
|
453
|
+
* Renders component groups (e.g. "Choose your base", "Pick toppings") with
|
|
454
|
+
* min/max selection constraints. Calculates real-time pricing via the SDK.
|
|
455
|
+
*
|
|
456
|
+
* Accepts pre-fetched `CompositeGroupView[]` from the product detail response
|
|
457
|
+
* — no internal data fetching, only live pricing calls.
|
|
458
|
+
*/
|
|
459
|
+
declare function CompositeSelector({ compositeId, groups, onSelectionsChange, onPriceChange, onReady, className, }: CompositeSelectorProps): React.ReactElement | null;
|
|
460
|
+
|
|
461
|
+
interface ProductCustomizerProps {
|
|
462
|
+
product: ProductWithDetails;
|
|
463
|
+
onAddToCart?: (product: ProductWithDetails, quantity: number, options: AddToCartOptions) => void | Promise<void>;
|
|
464
|
+
className?: string;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* ProductCustomizer — full product customization + add-to-cart flow.
|
|
468
|
+
*
|
|
469
|
+
* Auto-detects product type and routes to CompositeSelector / BundleSelector /
|
|
470
|
+
* VariantSelector + AddOnSelector. Ships a built-in "Add to Cart" button with
|
|
471
|
+
* QuantitySelector. When `onAddToCart` is provided it calls that instead of
|
|
472
|
+
* using the SDK cart (escape hatch for custom logic like toasts).
|
|
473
|
+
*/
|
|
474
|
+
declare function ProductCustomizer({ product, onAddToCart, className, }: ProductCustomizerProps): React.ReactElement;
|
|
475
|
+
|
|
423
476
|
interface ProductImageGalleryProps {
|
|
424
477
|
images: string[];
|
|
425
478
|
productName: string;
|
|
@@ -448,4 +501,4 @@ interface CartSummaryProps {
|
|
|
448
501
|
*/
|
|
449
502
|
declare function CartSummary({ onCheckout, onItemRemove, onQuantityChange, emptyMessage, className, }: CartSummaryProps): React.ReactElement;
|
|
450
503
|
|
|
451
|
-
export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, AddOnSelector, type AddOnSelectorProps, type AddToCartOptions, AddressElement, AuthElement, CartSummary, type CartSummaryProps, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, ElementsProvider, PaymentElement, Price, type PriceProps, ProductImageGallery, type ProductImageGalleryProps, QuantitySelector, type QuantitySelectorProps, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, VariantSelector, type VariantSelectorProps, useAds, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
|
|
504
|
+
export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, AddOnSelector, type AddOnSelectorProps, type AddToCartOptions, AddressElement, AuthElement, BundleSelector, type BundleSelectorProps, CartSummary, type CartSummaryProps, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, CompositeSelector, type CompositeSelectorProps, ElementsProvider, PaymentElement, Price, type PriceProps, ProductCustomizer, type ProductCustomizerProps, ProductImageGallery, type ProductImageGalleryProps, QuantitySelector, type QuantitySelectorProps, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, VariantSelector, type VariantSelectorProps, useAds, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
|
package/dist/react.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';
|
|
|
2
2
|
import { A as AdSlot, a as AdPosition, e as AdContextValue } from './ads-MkGm5l1T.js';
|
|
3
3
|
export { c as AdConfig, g as getVariantDisplayName } from './ads-MkGm5l1T.js';
|
|
4
4
|
import { C as CimplifyClient, Y as ProcessCheckoutResult, _ as CheckoutStatus, $ as CheckoutStatusContext, bq as Location, bn as Business, aS as Order, d as QuoteBundleSelectionInput, Q as QuoteCompositeSelectionInput, P as PriceQuote, g as QuoteUiMessage, w as ElementsOptions, cK as AuthenticatedData, cG as AddressInfo, cH as PaymentMethodInfo, s as CimplifyElements, cM as ElementsCheckoutResult, X as ProcessCheckoutOptions } from './client-CYRL8s1S.js';
|
|
5
|
-
import { s as Product, d as Pagination, h as CimplifyError, t as ProductWithDetails, N as Category, aE as BundleSelectionInput, af as ComponentSelectionInput, Q as Collection, C as CurrencyCode, u as ProductVariant, x as VariantAxisWithValues, M as Money, H as AddOnWithOptions } from './payment-D58dS_E9.js';
|
|
5
|
+
import { s as Product, d as Pagination, h as CimplifyError, t as ProductWithDetails, N as Category, aE as BundleSelectionInput, af as ComponentSelectionInput, Q as Collection, C as CurrencyCode, u as ProductVariant, x as VariantAxisWithValues, M as Money, H as AddOnWithOptions, a1 as BundleComponentView, ac as CompositeGroupView, ag as CompositePriceResult } from './payment-D58dS_E9.js';
|
|
6
6
|
|
|
7
7
|
interface UserIdentity {
|
|
8
8
|
uid: string;
|
|
@@ -420,6 +420,59 @@ interface AddOnSelectorProps {
|
|
|
420
420
|
*/
|
|
421
421
|
declare function AddOnSelector({ addOns, selectedOptions, onOptionsChange, className, }: AddOnSelectorProps): React.ReactElement | null;
|
|
422
422
|
|
|
423
|
+
interface BundleSelectorProps {
|
|
424
|
+
components: BundleComponentView[];
|
|
425
|
+
bundlePrice?: Money;
|
|
426
|
+
discountValue?: Money;
|
|
427
|
+
onSelectionsChange: (selections: BundleSelectionInput[]) => void;
|
|
428
|
+
onReady?: (ready: boolean) => void;
|
|
429
|
+
className?: string;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* BundleSelector — curated product set with optional variant choices.
|
|
433
|
+
*
|
|
434
|
+
* Each bundle component shows the included product. If `allow_variant_choice`
|
|
435
|
+
* is true, the customer can pick which variant they want for that slot.
|
|
436
|
+
*
|
|
437
|
+
* Accepts pre-fetched `BundleComponentView[]` from the product detail response
|
|
438
|
+
* — no internal data fetching.
|
|
439
|
+
*/
|
|
440
|
+
declare function BundleSelector({ components, bundlePrice, discountValue, onSelectionsChange, onReady, className, }: BundleSelectorProps): React.ReactElement | null;
|
|
441
|
+
|
|
442
|
+
interface CompositeSelectorProps {
|
|
443
|
+
compositeId: string;
|
|
444
|
+
groups: CompositeGroupView[];
|
|
445
|
+
onSelectionsChange: (selections: ComponentSelectionInput[]) => void;
|
|
446
|
+
onPriceChange?: (price: CompositePriceResult | null) => void;
|
|
447
|
+
onReady?: (ready: boolean) => void;
|
|
448
|
+
className?: string;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* CompositeSelector — "build your own" product configurator.
|
|
452
|
+
*
|
|
453
|
+
* Renders component groups (e.g. "Choose your base", "Pick toppings") with
|
|
454
|
+
* min/max selection constraints. Calculates real-time pricing via the SDK.
|
|
455
|
+
*
|
|
456
|
+
* Accepts pre-fetched `CompositeGroupView[]` from the product detail response
|
|
457
|
+
* — no internal data fetching, only live pricing calls.
|
|
458
|
+
*/
|
|
459
|
+
declare function CompositeSelector({ compositeId, groups, onSelectionsChange, onPriceChange, onReady, className, }: CompositeSelectorProps): React.ReactElement | null;
|
|
460
|
+
|
|
461
|
+
interface ProductCustomizerProps {
|
|
462
|
+
product: ProductWithDetails;
|
|
463
|
+
onAddToCart?: (product: ProductWithDetails, quantity: number, options: AddToCartOptions) => void | Promise<void>;
|
|
464
|
+
className?: string;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* ProductCustomizer — full product customization + add-to-cart flow.
|
|
468
|
+
*
|
|
469
|
+
* Auto-detects product type and routes to CompositeSelector / BundleSelector /
|
|
470
|
+
* VariantSelector + AddOnSelector. Ships a built-in "Add to Cart" button with
|
|
471
|
+
* QuantitySelector. When `onAddToCart` is provided it calls that instead of
|
|
472
|
+
* using the SDK cart (escape hatch for custom logic like toasts).
|
|
473
|
+
*/
|
|
474
|
+
declare function ProductCustomizer({ product, onAddToCart, className, }: ProductCustomizerProps): React.ReactElement;
|
|
475
|
+
|
|
423
476
|
interface ProductImageGalleryProps {
|
|
424
477
|
images: string[];
|
|
425
478
|
productName: string;
|
|
@@ -448,4 +501,4 @@ interface CartSummaryProps {
|
|
|
448
501
|
*/
|
|
449
502
|
declare function CartSummary({ onCheckout, onItemRemove, onQuantityChange, emptyMessage, className, }: CartSummaryProps): React.ReactElement;
|
|
450
503
|
|
|
451
|
-
export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, AddOnSelector, type AddOnSelectorProps, type AddToCartOptions, AddressElement, AuthElement, CartSummary, type CartSummaryProps, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, ElementsProvider, PaymentElement, Price, type PriceProps, ProductImageGallery, type ProductImageGalleryProps, QuantitySelector, type QuantitySelectorProps, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, VariantSelector, type VariantSelectorProps, useAds, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
|
|
504
|
+
export { Ad, AdPosition, type AdProps, AdProvider, AdSlot, AddOnSelector, type AddOnSelectorProps, type AddToCartOptions, AddressElement, AuthElement, BundleSelector, type BundleSelectorProps, CartSummary, type CartSummaryProps, CimplifyCheckout, type CimplifyCheckoutProps, type CimplifyContextValue, CimplifyProvider, type CimplifyProviderProps, CompositeSelector, type CompositeSelectorProps, ElementsProvider, PaymentElement, Price, type PriceProps, ProductCustomizer, type ProductCustomizerProps, ProductImageGallery, type ProductImageGalleryProps, QuantitySelector, type QuantitySelectorProps, type UseCartItem, type UseCartOptions, type UseCartResult, type UseCategoriesOptions, type UseCategoriesResult, type UseCollectionOptions, type UseCollectionResult, type UseCollectionsOptions, type UseCollectionsResult, type UseLocationsOptions, type UseLocationsResult, type UseOrderOptions, type UseOrderResult, type UseProductOptions, type UseProductResult, type UseProductsOptions, type UseProductsResult, type UseQuoteInput, type UseQuoteOptions, type UseQuoteResult, type UseSearchOptions, type UseSearchResult, VariantSelector, type VariantSelectorProps, useAds, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
|
package/dist/react.js
CHANGED
|
@@ -6752,6 +6752,522 @@ function AddOnSelector({
|
|
|
6752
6752
|
] }, addOn.id);
|
|
6753
6753
|
}) });
|
|
6754
6754
|
}
|
|
6755
|
+
function BundleSelector({
|
|
6756
|
+
components,
|
|
6757
|
+
bundlePrice,
|
|
6758
|
+
discountValue,
|
|
6759
|
+
onSelectionsChange,
|
|
6760
|
+
onReady,
|
|
6761
|
+
className
|
|
6762
|
+
}) {
|
|
6763
|
+
const [variantChoices, setVariantChoices] = React3.useState({});
|
|
6764
|
+
React3.useEffect(() => {
|
|
6765
|
+
const defaults = {};
|
|
6766
|
+
for (const comp of components) {
|
|
6767
|
+
if (comp.variant_id) {
|
|
6768
|
+
defaults[comp.id] = comp.variant_id;
|
|
6769
|
+
} else if (comp.available_variants.length > 0) {
|
|
6770
|
+
const defaultVariant = comp.available_variants.find((v) => v.is_default) || comp.available_variants[0];
|
|
6771
|
+
if (defaultVariant) {
|
|
6772
|
+
defaults[comp.id] = defaultVariant.id;
|
|
6773
|
+
}
|
|
6774
|
+
}
|
|
6775
|
+
}
|
|
6776
|
+
setVariantChoices(defaults);
|
|
6777
|
+
}, [components]);
|
|
6778
|
+
const selections = React3.useMemo(() => {
|
|
6779
|
+
return components.map((comp) => ({
|
|
6780
|
+
component_id: comp.id,
|
|
6781
|
+
variant_id: variantChoices[comp.id],
|
|
6782
|
+
quantity: comp.quantity
|
|
6783
|
+
}));
|
|
6784
|
+
}, [components, variantChoices]);
|
|
6785
|
+
React3.useEffect(() => {
|
|
6786
|
+
onSelectionsChange(selections);
|
|
6787
|
+
}, [selections, onSelectionsChange]);
|
|
6788
|
+
React3.useEffect(() => {
|
|
6789
|
+
onReady?.(components.length > 0 && selections.length > 0);
|
|
6790
|
+
}, [components, selections, onReady]);
|
|
6791
|
+
const handleVariantChange = React3.useCallback(
|
|
6792
|
+
(componentId, variantId) => {
|
|
6793
|
+
setVariantChoices((prev) => ({ ...prev, [componentId]: variantId }));
|
|
6794
|
+
},
|
|
6795
|
+
[]
|
|
6796
|
+
);
|
|
6797
|
+
if (components.length === 0) {
|
|
6798
|
+
return null;
|
|
6799
|
+
}
|
|
6800
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-bundle-selector": true, className, children: [
|
|
6801
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-bundle-heading": true, children: "Included in this bundle" }),
|
|
6802
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-bundle-components": true, children: components.map((comp) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
6803
|
+
BundleComponentCard,
|
|
6804
|
+
{
|
|
6805
|
+
component: comp,
|
|
6806
|
+
selectedVariantId: variantChoices[comp.id],
|
|
6807
|
+
onVariantChange: (variantId) => handleVariantChange(comp.id, variantId)
|
|
6808
|
+
},
|
|
6809
|
+
comp.id
|
|
6810
|
+
)) }),
|
|
6811
|
+
bundlePrice && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-bundle-summary": true, children: [
|
|
6812
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Bundle price" }),
|
|
6813
|
+
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: bundlePrice })
|
|
6814
|
+
] }),
|
|
6815
|
+
discountValue && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-bundle-savings": true, children: [
|
|
6816
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "You save" }),
|
|
6817
|
+
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: discountValue })
|
|
6818
|
+
] })
|
|
6819
|
+
] });
|
|
6820
|
+
}
|
|
6821
|
+
function BundleComponentCard({
|
|
6822
|
+
component,
|
|
6823
|
+
selectedVariantId,
|
|
6824
|
+
onVariantChange
|
|
6825
|
+
}) {
|
|
6826
|
+
const showVariantPicker = component.allow_variant_choice && component.available_variants.length > 1;
|
|
6827
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-bundle-component": true, children: [
|
|
6828
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-bundle-component-header": true, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
6829
|
+
component.quantity > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-bundle-component-qty": true, children: [
|
|
6830
|
+
"\xD7",
|
|
6831
|
+
component.quantity
|
|
6832
|
+
] }),
|
|
6833
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-bundle-component-name": true, children: component.product_name })
|
|
6834
|
+
] }) }),
|
|
6835
|
+
showVariantPicker && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-bundle-variant-picker": true, children: component.available_variants.map((variant) => {
|
|
6836
|
+
const isSelected = selectedVariantId === variant.id;
|
|
6837
|
+
const adjustment = parsePrice(variant.price_adjustment);
|
|
6838
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6839
|
+
"button",
|
|
6840
|
+
{
|
|
6841
|
+
type: "button",
|
|
6842
|
+
"aria-selected": isSelected,
|
|
6843
|
+
onClick: () => onVariantChange(variant.id),
|
|
6844
|
+
"data-cimplify-bundle-variant-option": true,
|
|
6845
|
+
"data-selected": isSelected || void 0,
|
|
6846
|
+
children: [
|
|
6847
|
+
variant.display_name,
|
|
6848
|
+
adjustment !== 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-bundle-variant-adjustment": true, children: [
|
|
6849
|
+
adjustment > 0 ? "+" : "",
|
|
6850
|
+
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: variant.price_adjustment })
|
|
6851
|
+
] })
|
|
6852
|
+
]
|
|
6853
|
+
},
|
|
6854
|
+
variant.id
|
|
6855
|
+
);
|
|
6856
|
+
}) })
|
|
6857
|
+
] });
|
|
6858
|
+
}
|
|
6859
|
+
function CompositeSelector({
|
|
6860
|
+
compositeId,
|
|
6861
|
+
groups,
|
|
6862
|
+
onSelectionsChange,
|
|
6863
|
+
onPriceChange,
|
|
6864
|
+
onReady,
|
|
6865
|
+
className
|
|
6866
|
+
}) {
|
|
6867
|
+
const { client } = useCimplify();
|
|
6868
|
+
const [groupSelections, setGroupSelections] = React3.useState({});
|
|
6869
|
+
const [priceResult, setPriceResult] = React3.useState(null);
|
|
6870
|
+
const [isPriceLoading, setIsPriceLoading] = React3.useState(false);
|
|
6871
|
+
const selections = React3.useMemo(() => {
|
|
6872
|
+
const result = [];
|
|
6873
|
+
for (const groupSels of Object.values(groupSelections)) {
|
|
6874
|
+
for (const [componentId, qty] of Object.entries(groupSels)) {
|
|
6875
|
+
if (qty > 0) {
|
|
6876
|
+
result.push({ component_id: componentId, quantity: qty });
|
|
6877
|
+
}
|
|
6878
|
+
}
|
|
6879
|
+
}
|
|
6880
|
+
return result;
|
|
6881
|
+
}, [groupSelections]);
|
|
6882
|
+
React3.useEffect(() => {
|
|
6883
|
+
onSelectionsChange(selections);
|
|
6884
|
+
}, [selections, onSelectionsChange]);
|
|
6885
|
+
React3.useEffect(() => {
|
|
6886
|
+
onPriceChange?.(priceResult);
|
|
6887
|
+
}, [priceResult, onPriceChange]);
|
|
6888
|
+
const allGroupsSatisfied = React3.useMemo(() => {
|
|
6889
|
+
for (const group of groups) {
|
|
6890
|
+
const groupSels = groupSelections[group.id] || {};
|
|
6891
|
+
const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
|
|
6892
|
+
if (totalSelected < group.min_selections) return false;
|
|
6893
|
+
}
|
|
6894
|
+
return true;
|
|
6895
|
+
}, [groups, groupSelections]);
|
|
6896
|
+
React3.useEffect(() => {
|
|
6897
|
+
onReady?.(allGroupsSatisfied);
|
|
6898
|
+
}, [allGroupsSatisfied, onReady]);
|
|
6899
|
+
React3.useEffect(() => {
|
|
6900
|
+
if (!allGroupsSatisfied || selections.length === 0) return;
|
|
6901
|
+
let cancelled = false;
|
|
6902
|
+
setIsPriceLoading(true);
|
|
6903
|
+
void client.catalogue.calculateCompositePrice(compositeId, selections).then((result) => {
|
|
6904
|
+
if (cancelled) return;
|
|
6905
|
+
if (result.ok) {
|
|
6906
|
+
setPriceResult(result.value);
|
|
6907
|
+
}
|
|
6908
|
+
}).finally(() => {
|
|
6909
|
+
if (!cancelled) setIsPriceLoading(false);
|
|
6910
|
+
});
|
|
6911
|
+
return () => {
|
|
6912
|
+
cancelled = true;
|
|
6913
|
+
};
|
|
6914
|
+
}, [selections, allGroupsSatisfied, compositeId, client]);
|
|
6915
|
+
const toggleComponent = React3.useCallback(
|
|
6916
|
+
(group, component) => {
|
|
6917
|
+
setGroupSelections((prev) => {
|
|
6918
|
+
const groupSels = { ...prev[group.id] || {} };
|
|
6919
|
+
const currentQty = groupSels[component.id] || 0;
|
|
6920
|
+
if (currentQty > 0) {
|
|
6921
|
+
if (group.min_selections > 0) {
|
|
6922
|
+
const totalOthers = Object.entries(groupSels).filter(([id]) => id !== component.id).reduce((sum, [, q]) => sum + q, 0);
|
|
6923
|
+
if (totalOthers < group.min_selections) {
|
|
6924
|
+
return prev;
|
|
6925
|
+
}
|
|
6926
|
+
}
|
|
6927
|
+
delete groupSels[component.id];
|
|
6928
|
+
} else {
|
|
6929
|
+
const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
|
|
6930
|
+
if (group.max_selections && totalSelected >= group.max_selections) {
|
|
6931
|
+
if (group.max_selections === 1) {
|
|
6932
|
+
return { ...prev, [group.id]: { [component.id]: 1 } };
|
|
6933
|
+
}
|
|
6934
|
+
return prev;
|
|
6935
|
+
}
|
|
6936
|
+
groupSels[component.id] = 1;
|
|
6937
|
+
}
|
|
6938
|
+
return { ...prev, [group.id]: groupSels };
|
|
6939
|
+
});
|
|
6940
|
+
},
|
|
6941
|
+
[]
|
|
6942
|
+
);
|
|
6943
|
+
const updateQuantity = React3.useCallback(
|
|
6944
|
+
(group, componentId, delta) => {
|
|
6945
|
+
setGroupSelections((prev) => {
|
|
6946
|
+
const groupSels = { ...prev[group.id] || {} };
|
|
6947
|
+
const current = groupSels[componentId] || 0;
|
|
6948
|
+
const next = Math.max(0, current + delta);
|
|
6949
|
+
if (group.max_quantity_per_component && next > group.max_quantity_per_component) {
|
|
6950
|
+
return prev;
|
|
6951
|
+
}
|
|
6952
|
+
if (next === 0) {
|
|
6953
|
+
delete groupSels[componentId];
|
|
6954
|
+
} else {
|
|
6955
|
+
groupSels[componentId] = next;
|
|
6956
|
+
}
|
|
6957
|
+
return { ...prev, [group.id]: groupSels };
|
|
6958
|
+
});
|
|
6959
|
+
},
|
|
6960
|
+
[]
|
|
6961
|
+
);
|
|
6962
|
+
if (groups.length === 0) {
|
|
6963
|
+
return null;
|
|
6964
|
+
}
|
|
6965
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-selector": true, className, children: [
|
|
6966
|
+
[...groups].sort((a, b) => a.display_order - b.display_order).map((group) => {
|
|
6967
|
+
const groupSels = groupSelections[group.id] || {};
|
|
6968
|
+
const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
|
|
6969
|
+
const minMet = totalSelected >= group.min_selections;
|
|
6970
|
+
const isSingleSelect = group.max_selections === 1;
|
|
6971
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-group": true, children: [
|
|
6972
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-group-header": true, children: [
|
|
6973
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
6974
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-composite-group-name": true, children: [
|
|
6975
|
+
group.name,
|
|
6976
|
+
group.min_selections > 0 && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-required": true, children: " *" })
|
|
6977
|
+
] }),
|
|
6978
|
+
group.description && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-group-description": true, children: group.description }),
|
|
6979
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-group-constraint": true, children: group.min_selections > 0 && group.max_selections ? `Choose ${group.min_selections}\u2013${group.max_selections}` : group.min_selections > 0 ? `Choose at least ${group.min_selections}` : group.max_selections ? `Choose up to ${group.max_selections}` : "Choose as many as you like" })
|
|
6980
|
+
] }),
|
|
6981
|
+
!minMet && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-validation": true, children: "Required" })
|
|
6982
|
+
] }),
|
|
6983
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-composite-components": true, children: group.components.filter((c) => c.is_available && !c.is_archived).sort((a, b) => a.display_order - b.display_order).map((component) => {
|
|
6984
|
+
const qty = groupSels[component.id] || 0;
|
|
6985
|
+
const isSelected = qty > 0;
|
|
6986
|
+
const displayName = component.display_name || component.id;
|
|
6987
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6988
|
+
"button",
|
|
6989
|
+
{
|
|
6990
|
+
type: "button",
|
|
6991
|
+
role: isSingleSelect ? "radio" : "checkbox",
|
|
6992
|
+
"aria-checked": isSelected,
|
|
6993
|
+
onClick: () => toggleComponent(group, component),
|
|
6994
|
+
"data-cimplify-composite-component": true,
|
|
6995
|
+
"data-selected": isSelected || void 0,
|
|
6996
|
+
children: [
|
|
6997
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-component-info": true, children: [
|
|
6998
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-component-name": true, children: displayName }),
|
|
6999
|
+
component.is_popular && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-badge": "popular", children: "Popular" }),
|
|
7000
|
+
component.is_premium && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-badge": "premium", children: "Premium" }),
|
|
7001
|
+
component.display_description && /* @__PURE__ */ jsxRuntime.jsx("span", { "data-cimplify-composite-component-description": true, children: component.display_description }),
|
|
7002
|
+
component.calories != null && /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-cimplify-composite-component-calories": true, children: [
|
|
7003
|
+
component.calories,
|
|
7004
|
+
" cal"
|
|
7005
|
+
] })
|
|
7006
|
+
] }),
|
|
7007
|
+
group.allow_quantity && isSelected && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
7008
|
+
"span",
|
|
7009
|
+
{
|
|
7010
|
+
"data-cimplify-composite-qty": true,
|
|
7011
|
+
onClick: (e) => e.stopPropagation(),
|
|
7012
|
+
children: [
|
|
7013
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7014
|
+
"button",
|
|
7015
|
+
{
|
|
7016
|
+
type: "button",
|
|
7017
|
+
onClick: () => updateQuantity(group, component.id, -1),
|
|
7018
|
+
"aria-label": `Decrease ${displayName} quantity`,
|
|
7019
|
+
children: "\u2212"
|
|
7020
|
+
}
|
|
7021
|
+
),
|
|
7022
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: qty }),
|
|
7023
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7024
|
+
"button",
|
|
7025
|
+
{
|
|
7026
|
+
type: "button",
|
|
7027
|
+
onClick: () => updateQuantity(group, component.id, 1),
|
|
7028
|
+
"aria-label": `Increase ${displayName} quantity`,
|
|
7029
|
+
children: "+"
|
|
7030
|
+
}
|
|
7031
|
+
)
|
|
7032
|
+
]
|
|
7033
|
+
}
|
|
7034
|
+
),
|
|
7035
|
+
component.price && component.price !== "0" && /* @__PURE__ */ jsxRuntime.jsx(Price, { amount: component.price, prefix: "+" })
|
|
7036
|
+
]
|
|
7037
|
+
},
|
|
7038
|
+
component.id
|
|
7039
|
+
);
|
|
7040
|
+
}) })
|
|
7041
|
+
] }, group.id);
|
|
7042
|
+
}),
|
|
7043
|
+
priceResult && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-summary": true, children: [
|
|
7044
|
+
priceResult.base_price !== "0" && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-summary-line": true, children: [
|
|
7045
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Base" }),
|
|
7046
|
+
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: priceResult.base_price })
|
|
7047
|
+
] }),
|
|
7048
|
+
priceResult.components_total !== "0" && /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-summary-line": true, children: [
|
|
7049
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Selections" }),
|
|
7050
|
+
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: priceResult.components_total })
|
|
7051
|
+
] }),
|
|
7052
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-composite-summary-total": true, children: [
|
|
7053
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Total" }),
|
|
7054
|
+
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: priceResult.final_price })
|
|
7055
|
+
] })
|
|
7056
|
+
] }),
|
|
7057
|
+
isPriceLoading && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-cimplify-composite-calculating": true, children: "Calculating price..." })
|
|
7058
|
+
] });
|
|
7059
|
+
}
|
|
7060
|
+
function ProductCustomizer({
|
|
7061
|
+
product,
|
|
7062
|
+
onAddToCart,
|
|
7063
|
+
className
|
|
7064
|
+
}) {
|
|
7065
|
+
const [quantity, setQuantity] = React3.useState(1);
|
|
7066
|
+
const [isAdded, setIsAdded] = React3.useState(false);
|
|
7067
|
+
const [isSubmitting, setIsSubmitting] = React3.useState(false);
|
|
7068
|
+
const [selectedVariantId, setSelectedVariantId] = React3.useState();
|
|
7069
|
+
const [selectedVariant, setSelectedVariant] = React3.useState();
|
|
7070
|
+
const [selectedAddOnOptionIds, setSelectedAddOnOptionIds] = React3.useState([]);
|
|
7071
|
+
const [compositeSelections, setCompositeSelections] = React3.useState([]);
|
|
7072
|
+
const [compositePrice, setCompositePrice] = React3.useState(null);
|
|
7073
|
+
const [compositeReady, setCompositeReady] = React3.useState(false);
|
|
7074
|
+
const [bundleSelections, setBundleSelections] = React3.useState([]);
|
|
7075
|
+
const [bundleReady, setBundleReady] = React3.useState(false);
|
|
7076
|
+
const cart = useCart();
|
|
7077
|
+
const productType = product.type || "product";
|
|
7078
|
+
const isComposite = productType === "composite";
|
|
7079
|
+
const isBundle = productType === "bundle";
|
|
7080
|
+
const isStandard = !isComposite && !isBundle;
|
|
7081
|
+
const hasVariants = isStandard && product.variants && product.variants.length > 0;
|
|
7082
|
+
const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;
|
|
7083
|
+
React3.useEffect(() => {
|
|
7084
|
+
setQuantity(1);
|
|
7085
|
+
setIsAdded(false);
|
|
7086
|
+
setIsSubmitting(false);
|
|
7087
|
+
setSelectedVariantId(void 0);
|
|
7088
|
+
setSelectedVariant(void 0);
|
|
7089
|
+
setSelectedAddOnOptionIds([]);
|
|
7090
|
+
setCompositeSelections([]);
|
|
7091
|
+
setCompositePrice(null);
|
|
7092
|
+
setCompositeReady(false);
|
|
7093
|
+
setBundleSelections([]);
|
|
7094
|
+
setBundleReady(false);
|
|
7095
|
+
}, [product.id]);
|
|
7096
|
+
const selectedAddOnOptions = React3.useMemo(() => {
|
|
7097
|
+
if (!product.add_ons) return [];
|
|
7098
|
+
const options = [];
|
|
7099
|
+
for (const addOn of product.add_ons) {
|
|
7100
|
+
for (const option of addOn.options) {
|
|
7101
|
+
if (selectedAddOnOptionIds.includes(option.id)) {
|
|
7102
|
+
options.push(option);
|
|
7103
|
+
}
|
|
7104
|
+
}
|
|
7105
|
+
}
|
|
7106
|
+
return options;
|
|
7107
|
+
}, [product.add_ons, selectedAddOnOptionIds]);
|
|
7108
|
+
const normalizedAddOnOptionIds = React3.useMemo(() => {
|
|
7109
|
+
if (selectedAddOnOptionIds.length === 0) return [];
|
|
7110
|
+
return Array.from(new Set(selectedAddOnOptionIds.map((id) => id.trim()).filter(Boolean))).sort();
|
|
7111
|
+
}, [selectedAddOnOptionIds]);
|
|
7112
|
+
const localTotalPrice = React3.useMemo(() => {
|
|
7113
|
+
if (isComposite && compositePrice) {
|
|
7114
|
+
return parsePrice(compositePrice.final_price) * quantity;
|
|
7115
|
+
}
|
|
7116
|
+
let price = parsePrice(product.default_price);
|
|
7117
|
+
if (selectedVariant?.price_adjustment) {
|
|
7118
|
+
price += parsePrice(selectedVariant.price_adjustment);
|
|
7119
|
+
}
|
|
7120
|
+
for (const option of selectedAddOnOptions) {
|
|
7121
|
+
if (option.default_price) {
|
|
7122
|
+
price += parsePrice(option.default_price);
|
|
7123
|
+
}
|
|
7124
|
+
}
|
|
7125
|
+
return price * quantity;
|
|
7126
|
+
}, [product.default_price, selectedVariant, selectedAddOnOptions, quantity, isComposite, compositePrice]);
|
|
7127
|
+
const requiredAddOnsSatisfied = React3.useMemo(() => {
|
|
7128
|
+
if (!product.add_ons) return true;
|
|
7129
|
+
for (const addOn of product.add_ons) {
|
|
7130
|
+
if (addOn.is_required) {
|
|
7131
|
+
const selectedInGroup = selectedAddOnOptionIds.filter(
|
|
7132
|
+
(id) => addOn.options.some((opt) => opt.id === id)
|
|
7133
|
+
).length;
|
|
7134
|
+
const minRequired = addOn.min_selections || 1;
|
|
7135
|
+
if (selectedInGroup < minRequired) {
|
|
7136
|
+
return false;
|
|
7137
|
+
}
|
|
7138
|
+
}
|
|
7139
|
+
}
|
|
7140
|
+
return true;
|
|
7141
|
+
}, [product.add_ons, selectedAddOnOptionIds]);
|
|
7142
|
+
const quoteInput = React3.useMemo(
|
|
7143
|
+
() => ({
|
|
7144
|
+
productId: product.id,
|
|
7145
|
+
quantity,
|
|
7146
|
+
variantId: selectedVariantId,
|
|
7147
|
+
addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : void 0
|
|
7148
|
+
}),
|
|
7149
|
+
[product.id, quantity, selectedVariantId, normalizedAddOnOptionIds]
|
|
7150
|
+
);
|
|
7151
|
+
const { quote } = useQuote(quoteInput, {
|
|
7152
|
+
enabled: isStandard && requiredAddOnsSatisfied
|
|
7153
|
+
});
|
|
7154
|
+
const quoteId = quote?.quote_id;
|
|
7155
|
+
const quotedTotalPrice = React3.useMemo(() => {
|
|
7156
|
+
if (!quote) return void 0;
|
|
7157
|
+
const quotedTotal = quote.quoted_total_price_info?.final_price ?? quote.final_price_info.final_price;
|
|
7158
|
+
return quotedTotal === void 0 || quotedTotal === null ? void 0 : parsePrice(quotedTotal);
|
|
7159
|
+
}, [quote]);
|
|
7160
|
+
const displayTotalPrice = quotedTotalPrice ?? localTotalPrice;
|
|
7161
|
+
const canAddToCart = React3.useMemo(() => {
|
|
7162
|
+
if (isComposite) return compositeReady;
|
|
7163
|
+
if (isBundle) return bundleReady;
|
|
7164
|
+
return requiredAddOnsSatisfied;
|
|
7165
|
+
}, [isComposite, isBundle, compositeReady, bundleReady, requiredAddOnsSatisfied]);
|
|
7166
|
+
const handleVariantChange = React3.useCallback(
|
|
7167
|
+
(variantId, variant) => {
|
|
7168
|
+
setSelectedVariantId(variantId);
|
|
7169
|
+
setSelectedVariant(variant);
|
|
7170
|
+
},
|
|
7171
|
+
[]
|
|
7172
|
+
);
|
|
7173
|
+
const handleAddToCart = async () => {
|
|
7174
|
+
if (isSubmitting) return;
|
|
7175
|
+
setIsSubmitting(true);
|
|
7176
|
+
const options = {
|
|
7177
|
+
variantId: selectedVariantId,
|
|
7178
|
+
variant: selectedVariant ? { id: selectedVariant.id, name: selectedVariant.name || "", price_adjustment: selectedVariant.price_adjustment } : void 0,
|
|
7179
|
+
quoteId,
|
|
7180
|
+
addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : void 0,
|
|
7181
|
+
addOnOptions: selectedAddOnOptions.length > 0 ? selectedAddOnOptions.map((opt) => ({
|
|
7182
|
+
id: opt.id,
|
|
7183
|
+
name: opt.name,
|
|
7184
|
+
add_on_id: opt.add_on_id,
|
|
7185
|
+
default_price: opt.default_price
|
|
7186
|
+
})) : void 0,
|
|
7187
|
+
compositeSelections: isComposite && compositeSelections.length > 0 ? compositeSelections : void 0,
|
|
7188
|
+
bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : void 0
|
|
7189
|
+
};
|
|
7190
|
+
try {
|
|
7191
|
+
if (onAddToCart) {
|
|
7192
|
+
await onAddToCart(product, quantity, options);
|
|
7193
|
+
} else {
|
|
7194
|
+
await cart.addItem(product, quantity, options);
|
|
7195
|
+
}
|
|
7196
|
+
setIsAdded(true);
|
|
7197
|
+
setTimeout(() => {
|
|
7198
|
+
setIsAdded(false);
|
|
7199
|
+
setQuantity(1);
|
|
7200
|
+
}, 2e3);
|
|
7201
|
+
} catch {
|
|
7202
|
+
} finally {
|
|
7203
|
+
setIsSubmitting(false);
|
|
7204
|
+
}
|
|
7205
|
+
};
|
|
7206
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer": true, className, children: [
|
|
7207
|
+
isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7208
|
+
CompositeSelector,
|
|
7209
|
+
{
|
|
7210
|
+
compositeId: product.composite_id,
|
|
7211
|
+
groups: product.groups,
|
|
7212
|
+
onSelectionsChange: setCompositeSelections,
|
|
7213
|
+
onPriceChange: setCompositePrice,
|
|
7214
|
+
onReady: setCompositeReady
|
|
7215
|
+
}
|
|
7216
|
+
),
|
|
7217
|
+
isBundle && product.components && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7218
|
+
BundleSelector,
|
|
7219
|
+
{
|
|
7220
|
+
components: product.components,
|
|
7221
|
+
bundlePrice: product.bundle_price,
|
|
7222
|
+
discountValue: product.discount_value,
|
|
7223
|
+
onSelectionsChange: setBundleSelections,
|
|
7224
|
+
onReady: setBundleReady
|
|
7225
|
+
}
|
|
7226
|
+
),
|
|
7227
|
+
hasVariants && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7228
|
+
VariantSelector,
|
|
7229
|
+
{
|
|
7230
|
+
variants: product.variants,
|
|
7231
|
+
variantAxes: product.variant_axes,
|
|
7232
|
+
basePrice: product.default_price,
|
|
7233
|
+
selectedVariantId,
|
|
7234
|
+
onVariantChange: handleVariantChange
|
|
7235
|
+
}
|
|
7236
|
+
),
|
|
7237
|
+
hasAddOns && /* @__PURE__ */ jsxRuntime.jsx(
|
|
7238
|
+
AddOnSelector,
|
|
7239
|
+
{
|
|
7240
|
+
addOns: product.add_ons,
|
|
7241
|
+
selectedOptions: selectedAddOnOptionIds,
|
|
7242
|
+
onOptionsChange: setSelectedAddOnOptionIds
|
|
7243
|
+
}
|
|
7244
|
+
),
|
|
7245
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cimplify-customizer-actions": true, children: [
|
|
7246
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7247
|
+
QuantitySelector,
|
|
7248
|
+
{
|
|
7249
|
+
value: quantity,
|
|
7250
|
+
onChange: setQuantity,
|
|
7251
|
+
min: 1
|
|
7252
|
+
}
|
|
7253
|
+
),
|
|
7254
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7255
|
+
"button",
|
|
7256
|
+
{
|
|
7257
|
+
type: "button",
|
|
7258
|
+
onClick: handleAddToCart,
|
|
7259
|
+
disabled: isAdded || isSubmitting || !canAddToCart,
|
|
7260
|
+
"data-cimplify-customizer-submit": true,
|
|
7261
|
+
children: isAdded ? "Added to Cart" : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7262
|
+
"Add to Cart \xB7 ",
|
|
7263
|
+
/* @__PURE__ */ jsxRuntime.jsx(Price, { amount: displayTotalPrice })
|
|
7264
|
+
] })
|
|
7265
|
+
}
|
|
7266
|
+
)
|
|
7267
|
+
] }),
|
|
7268
|
+
!canAddToCart && /* @__PURE__ */ jsxRuntime.jsx("p", { "data-cimplify-customizer-validation": true, children: "Please select all required options" })
|
|
7269
|
+
] });
|
|
7270
|
+
}
|
|
6755
7271
|
var ASPECT_STYLES = {
|
|
6756
7272
|
square: { aspectRatio: "1/1" },
|
|
6757
7273
|
"4/3": { aspectRatio: "4/3" },
|
|
@@ -6933,12 +7449,15 @@ exports.AdProvider = AdProvider;
|
|
|
6933
7449
|
exports.AddOnSelector = AddOnSelector;
|
|
6934
7450
|
exports.AddressElement = AddressElement;
|
|
6935
7451
|
exports.AuthElement = AuthElement;
|
|
7452
|
+
exports.BundleSelector = BundleSelector;
|
|
6936
7453
|
exports.CartSummary = CartSummary;
|
|
6937
7454
|
exports.CimplifyCheckout = CimplifyCheckout;
|
|
6938
7455
|
exports.CimplifyProvider = CimplifyProvider;
|
|
7456
|
+
exports.CompositeSelector = CompositeSelector;
|
|
6939
7457
|
exports.ElementsProvider = ElementsProvider;
|
|
6940
7458
|
exports.PaymentElement = PaymentElement;
|
|
6941
7459
|
exports.Price = Price;
|
|
7460
|
+
exports.ProductCustomizer = ProductCustomizer;
|
|
6942
7461
|
exports.ProductImageGallery = ProductImageGallery;
|
|
6943
7462
|
exports.QuantitySelector = QuantitySelector;
|
|
6944
7463
|
exports.VariantSelector = VariantSelector;
|
package/dist/react.mjs
CHANGED
|
@@ -6746,6 +6746,522 @@ function AddOnSelector({
|
|
|
6746
6746
|
] }, addOn.id);
|
|
6747
6747
|
}) });
|
|
6748
6748
|
}
|
|
6749
|
+
function BundleSelector({
|
|
6750
|
+
components,
|
|
6751
|
+
bundlePrice,
|
|
6752
|
+
discountValue,
|
|
6753
|
+
onSelectionsChange,
|
|
6754
|
+
onReady,
|
|
6755
|
+
className
|
|
6756
|
+
}) {
|
|
6757
|
+
const [variantChoices, setVariantChoices] = useState({});
|
|
6758
|
+
useEffect(() => {
|
|
6759
|
+
const defaults = {};
|
|
6760
|
+
for (const comp of components) {
|
|
6761
|
+
if (comp.variant_id) {
|
|
6762
|
+
defaults[comp.id] = comp.variant_id;
|
|
6763
|
+
} else if (comp.available_variants.length > 0) {
|
|
6764
|
+
const defaultVariant = comp.available_variants.find((v) => v.is_default) || comp.available_variants[0];
|
|
6765
|
+
if (defaultVariant) {
|
|
6766
|
+
defaults[comp.id] = defaultVariant.id;
|
|
6767
|
+
}
|
|
6768
|
+
}
|
|
6769
|
+
}
|
|
6770
|
+
setVariantChoices(defaults);
|
|
6771
|
+
}, [components]);
|
|
6772
|
+
const selections = useMemo(() => {
|
|
6773
|
+
return components.map((comp) => ({
|
|
6774
|
+
component_id: comp.id,
|
|
6775
|
+
variant_id: variantChoices[comp.id],
|
|
6776
|
+
quantity: comp.quantity
|
|
6777
|
+
}));
|
|
6778
|
+
}, [components, variantChoices]);
|
|
6779
|
+
useEffect(() => {
|
|
6780
|
+
onSelectionsChange(selections);
|
|
6781
|
+
}, [selections, onSelectionsChange]);
|
|
6782
|
+
useEffect(() => {
|
|
6783
|
+
onReady?.(components.length > 0 && selections.length > 0);
|
|
6784
|
+
}, [components, selections, onReady]);
|
|
6785
|
+
const handleVariantChange = useCallback(
|
|
6786
|
+
(componentId, variantId) => {
|
|
6787
|
+
setVariantChoices((prev) => ({ ...prev, [componentId]: variantId }));
|
|
6788
|
+
},
|
|
6789
|
+
[]
|
|
6790
|
+
);
|
|
6791
|
+
if (components.length === 0) {
|
|
6792
|
+
return null;
|
|
6793
|
+
}
|
|
6794
|
+
return /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-selector": true, className, children: [
|
|
6795
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-bundle-heading": true, children: "Included in this bundle" }),
|
|
6796
|
+
/* @__PURE__ */ jsx("div", { "data-cimplify-bundle-components": true, children: components.map((comp) => /* @__PURE__ */ jsx(
|
|
6797
|
+
BundleComponentCard,
|
|
6798
|
+
{
|
|
6799
|
+
component: comp,
|
|
6800
|
+
selectedVariantId: variantChoices[comp.id],
|
|
6801
|
+
onVariantChange: (variantId) => handleVariantChange(comp.id, variantId)
|
|
6802
|
+
},
|
|
6803
|
+
comp.id
|
|
6804
|
+
)) }),
|
|
6805
|
+
bundlePrice && /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-summary": true, children: [
|
|
6806
|
+
/* @__PURE__ */ jsx("span", { children: "Bundle price" }),
|
|
6807
|
+
/* @__PURE__ */ jsx(Price, { amount: bundlePrice })
|
|
6808
|
+
] }),
|
|
6809
|
+
discountValue && /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-savings": true, children: [
|
|
6810
|
+
/* @__PURE__ */ jsx("span", { children: "You save" }),
|
|
6811
|
+
/* @__PURE__ */ jsx(Price, { amount: discountValue })
|
|
6812
|
+
] })
|
|
6813
|
+
] });
|
|
6814
|
+
}
|
|
6815
|
+
function BundleComponentCard({
|
|
6816
|
+
component,
|
|
6817
|
+
selectedVariantId,
|
|
6818
|
+
onVariantChange
|
|
6819
|
+
}) {
|
|
6820
|
+
const showVariantPicker = component.allow_variant_choice && component.available_variants.length > 1;
|
|
6821
|
+
return /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-component": true, children: [
|
|
6822
|
+
/* @__PURE__ */ jsx("div", { "data-cimplify-bundle-component-header": true, children: /* @__PURE__ */ jsxs("div", { children: [
|
|
6823
|
+
component.quantity > 1 && /* @__PURE__ */ jsxs("span", { "data-cimplify-bundle-component-qty": true, children: [
|
|
6824
|
+
"\xD7",
|
|
6825
|
+
component.quantity
|
|
6826
|
+
] }),
|
|
6827
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-bundle-component-name": true, children: component.product_name })
|
|
6828
|
+
] }) }),
|
|
6829
|
+
showVariantPicker && /* @__PURE__ */ jsx("div", { "data-cimplify-bundle-variant-picker": true, children: component.available_variants.map((variant) => {
|
|
6830
|
+
const isSelected = selectedVariantId === variant.id;
|
|
6831
|
+
const adjustment = parsePrice(variant.price_adjustment);
|
|
6832
|
+
return /* @__PURE__ */ jsxs(
|
|
6833
|
+
"button",
|
|
6834
|
+
{
|
|
6835
|
+
type: "button",
|
|
6836
|
+
"aria-selected": isSelected,
|
|
6837
|
+
onClick: () => onVariantChange(variant.id),
|
|
6838
|
+
"data-cimplify-bundle-variant-option": true,
|
|
6839
|
+
"data-selected": isSelected || void 0,
|
|
6840
|
+
children: [
|
|
6841
|
+
variant.display_name,
|
|
6842
|
+
adjustment !== 0 && /* @__PURE__ */ jsxs("span", { "data-cimplify-bundle-variant-adjustment": true, children: [
|
|
6843
|
+
adjustment > 0 ? "+" : "",
|
|
6844
|
+
/* @__PURE__ */ jsx(Price, { amount: variant.price_adjustment })
|
|
6845
|
+
] })
|
|
6846
|
+
]
|
|
6847
|
+
},
|
|
6848
|
+
variant.id
|
|
6849
|
+
);
|
|
6850
|
+
}) })
|
|
6851
|
+
] });
|
|
6852
|
+
}
|
|
6853
|
+
function CompositeSelector({
|
|
6854
|
+
compositeId,
|
|
6855
|
+
groups,
|
|
6856
|
+
onSelectionsChange,
|
|
6857
|
+
onPriceChange,
|
|
6858
|
+
onReady,
|
|
6859
|
+
className
|
|
6860
|
+
}) {
|
|
6861
|
+
const { client } = useCimplify();
|
|
6862
|
+
const [groupSelections, setGroupSelections] = useState({});
|
|
6863
|
+
const [priceResult, setPriceResult] = useState(null);
|
|
6864
|
+
const [isPriceLoading, setIsPriceLoading] = useState(false);
|
|
6865
|
+
const selections = useMemo(() => {
|
|
6866
|
+
const result = [];
|
|
6867
|
+
for (const groupSels of Object.values(groupSelections)) {
|
|
6868
|
+
for (const [componentId, qty] of Object.entries(groupSels)) {
|
|
6869
|
+
if (qty > 0) {
|
|
6870
|
+
result.push({ component_id: componentId, quantity: qty });
|
|
6871
|
+
}
|
|
6872
|
+
}
|
|
6873
|
+
}
|
|
6874
|
+
return result;
|
|
6875
|
+
}, [groupSelections]);
|
|
6876
|
+
useEffect(() => {
|
|
6877
|
+
onSelectionsChange(selections);
|
|
6878
|
+
}, [selections, onSelectionsChange]);
|
|
6879
|
+
useEffect(() => {
|
|
6880
|
+
onPriceChange?.(priceResult);
|
|
6881
|
+
}, [priceResult, onPriceChange]);
|
|
6882
|
+
const allGroupsSatisfied = useMemo(() => {
|
|
6883
|
+
for (const group of groups) {
|
|
6884
|
+
const groupSels = groupSelections[group.id] || {};
|
|
6885
|
+
const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
|
|
6886
|
+
if (totalSelected < group.min_selections) return false;
|
|
6887
|
+
}
|
|
6888
|
+
return true;
|
|
6889
|
+
}, [groups, groupSelections]);
|
|
6890
|
+
useEffect(() => {
|
|
6891
|
+
onReady?.(allGroupsSatisfied);
|
|
6892
|
+
}, [allGroupsSatisfied, onReady]);
|
|
6893
|
+
useEffect(() => {
|
|
6894
|
+
if (!allGroupsSatisfied || selections.length === 0) return;
|
|
6895
|
+
let cancelled = false;
|
|
6896
|
+
setIsPriceLoading(true);
|
|
6897
|
+
void client.catalogue.calculateCompositePrice(compositeId, selections).then((result) => {
|
|
6898
|
+
if (cancelled) return;
|
|
6899
|
+
if (result.ok) {
|
|
6900
|
+
setPriceResult(result.value);
|
|
6901
|
+
}
|
|
6902
|
+
}).finally(() => {
|
|
6903
|
+
if (!cancelled) setIsPriceLoading(false);
|
|
6904
|
+
});
|
|
6905
|
+
return () => {
|
|
6906
|
+
cancelled = true;
|
|
6907
|
+
};
|
|
6908
|
+
}, [selections, allGroupsSatisfied, compositeId, client]);
|
|
6909
|
+
const toggleComponent = useCallback(
|
|
6910
|
+
(group, component) => {
|
|
6911
|
+
setGroupSelections((prev) => {
|
|
6912
|
+
const groupSels = { ...prev[group.id] || {} };
|
|
6913
|
+
const currentQty = groupSels[component.id] || 0;
|
|
6914
|
+
if (currentQty > 0) {
|
|
6915
|
+
if (group.min_selections > 0) {
|
|
6916
|
+
const totalOthers = Object.entries(groupSels).filter(([id]) => id !== component.id).reduce((sum, [, q]) => sum + q, 0);
|
|
6917
|
+
if (totalOthers < group.min_selections) {
|
|
6918
|
+
return prev;
|
|
6919
|
+
}
|
|
6920
|
+
}
|
|
6921
|
+
delete groupSels[component.id];
|
|
6922
|
+
} else {
|
|
6923
|
+
const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
|
|
6924
|
+
if (group.max_selections && totalSelected >= group.max_selections) {
|
|
6925
|
+
if (group.max_selections === 1) {
|
|
6926
|
+
return { ...prev, [group.id]: { [component.id]: 1 } };
|
|
6927
|
+
}
|
|
6928
|
+
return prev;
|
|
6929
|
+
}
|
|
6930
|
+
groupSels[component.id] = 1;
|
|
6931
|
+
}
|
|
6932
|
+
return { ...prev, [group.id]: groupSels };
|
|
6933
|
+
});
|
|
6934
|
+
},
|
|
6935
|
+
[]
|
|
6936
|
+
);
|
|
6937
|
+
const updateQuantity = useCallback(
|
|
6938
|
+
(group, componentId, delta) => {
|
|
6939
|
+
setGroupSelections((prev) => {
|
|
6940
|
+
const groupSels = { ...prev[group.id] || {} };
|
|
6941
|
+
const current = groupSels[componentId] || 0;
|
|
6942
|
+
const next = Math.max(0, current + delta);
|
|
6943
|
+
if (group.max_quantity_per_component && next > group.max_quantity_per_component) {
|
|
6944
|
+
return prev;
|
|
6945
|
+
}
|
|
6946
|
+
if (next === 0) {
|
|
6947
|
+
delete groupSels[componentId];
|
|
6948
|
+
} else {
|
|
6949
|
+
groupSels[componentId] = next;
|
|
6950
|
+
}
|
|
6951
|
+
return { ...prev, [group.id]: groupSels };
|
|
6952
|
+
});
|
|
6953
|
+
},
|
|
6954
|
+
[]
|
|
6955
|
+
);
|
|
6956
|
+
if (groups.length === 0) {
|
|
6957
|
+
return null;
|
|
6958
|
+
}
|
|
6959
|
+
return /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-selector": true, className, children: [
|
|
6960
|
+
[...groups].sort((a, b) => a.display_order - b.display_order).map((group) => {
|
|
6961
|
+
const groupSels = groupSelections[group.id] || {};
|
|
6962
|
+
const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
|
|
6963
|
+
const minMet = totalSelected >= group.min_selections;
|
|
6964
|
+
const isSingleSelect = group.max_selections === 1;
|
|
6965
|
+
return /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-group": true, children: [
|
|
6966
|
+
/* @__PURE__ */ jsxs("div", { "data-cimplify-composite-group-header": true, children: [
|
|
6967
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
6968
|
+
/* @__PURE__ */ jsxs("span", { "data-cimplify-composite-group-name": true, children: [
|
|
6969
|
+
group.name,
|
|
6970
|
+
group.min_selections > 0 && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-required": true, children: " *" })
|
|
6971
|
+
] }),
|
|
6972
|
+
group.description && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-group-description": true, children: group.description }),
|
|
6973
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-composite-group-constraint": true, children: group.min_selections > 0 && group.max_selections ? `Choose ${group.min_selections}\u2013${group.max_selections}` : group.min_selections > 0 ? `Choose at least ${group.min_selections}` : group.max_selections ? `Choose up to ${group.max_selections}` : "Choose as many as you like" })
|
|
6974
|
+
] }),
|
|
6975
|
+
!minMet && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-validation": true, children: "Required" })
|
|
6976
|
+
] }),
|
|
6977
|
+
/* @__PURE__ */ jsx("div", { "data-cimplify-composite-components": true, children: group.components.filter((c) => c.is_available && !c.is_archived).sort((a, b) => a.display_order - b.display_order).map((component) => {
|
|
6978
|
+
const qty = groupSels[component.id] || 0;
|
|
6979
|
+
const isSelected = qty > 0;
|
|
6980
|
+
const displayName = component.display_name || component.id;
|
|
6981
|
+
return /* @__PURE__ */ jsxs(
|
|
6982
|
+
"button",
|
|
6983
|
+
{
|
|
6984
|
+
type: "button",
|
|
6985
|
+
role: isSingleSelect ? "radio" : "checkbox",
|
|
6986
|
+
"aria-checked": isSelected,
|
|
6987
|
+
onClick: () => toggleComponent(group, component),
|
|
6988
|
+
"data-cimplify-composite-component": true,
|
|
6989
|
+
"data-selected": isSelected || void 0,
|
|
6990
|
+
children: [
|
|
6991
|
+
/* @__PURE__ */ jsxs("div", { "data-cimplify-composite-component-info": true, children: [
|
|
6992
|
+
/* @__PURE__ */ jsx("span", { "data-cimplify-composite-component-name": true, children: displayName }),
|
|
6993
|
+
component.is_popular && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-badge": "popular", children: "Popular" }),
|
|
6994
|
+
component.is_premium && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-badge": "premium", children: "Premium" }),
|
|
6995
|
+
component.display_description && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-component-description": true, children: component.display_description }),
|
|
6996
|
+
component.calories != null && /* @__PURE__ */ jsxs("span", { "data-cimplify-composite-component-calories": true, children: [
|
|
6997
|
+
component.calories,
|
|
6998
|
+
" cal"
|
|
6999
|
+
] })
|
|
7000
|
+
] }),
|
|
7001
|
+
group.allow_quantity && isSelected && /* @__PURE__ */ jsxs(
|
|
7002
|
+
"span",
|
|
7003
|
+
{
|
|
7004
|
+
"data-cimplify-composite-qty": true,
|
|
7005
|
+
onClick: (e) => e.stopPropagation(),
|
|
7006
|
+
children: [
|
|
7007
|
+
/* @__PURE__ */ jsx(
|
|
7008
|
+
"button",
|
|
7009
|
+
{
|
|
7010
|
+
type: "button",
|
|
7011
|
+
onClick: () => updateQuantity(group, component.id, -1),
|
|
7012
|
+
"aria-label": `Decrease ${displayName} quantity`,
|
|
7013
|
+
children: "\u2212"
|
|
7014
|
+
}
|
|
7015
|
+
),
|
|
7016
|
+
/* @__PURE__ */ jsx("span", { children: qty }),
|
|
7017
|
+
/* @__PURE__ */ jsx(
|
|
7018
|
+
"button",
|
|
7019
|
+
{
|
|
7020
|
+
type: "button",
|
|
7021
|
+
onClick: () => updateQuantity(group, component.id, 1),
|
|
7022
|
+
"aria-label": `Increase ${displayName} quantity`,
|
|
7023
|
+
children: "+"
|
|
7024
|
+
}
|
|
7025
|
+
)
|
|
7026
|
+
]
|
|
7027
|
+
}
|
|
7028
|
+
),
|
|
7029
|
+
component.price && component.price !== "0" && /* @__PURE__ */ jsx(Price, { amount: component.price, prefix: "+" })
|
|
7030
|
+
]
|
|
7031
|
+
},
|
|
7032
|
+
component.id
|
|
7033
|
+
);
|
|
7034
|
+
}) })
|
|
7035
|
+
] }, group.id);
|
|
7036
|
+
}),
|
|
7037
|
+
priceResult && /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary": true, children: [
|
|
7038
|
+
priceResult.base_price !== "0" && /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary-line": true, children: [
|
|
7039
|
+
/* @__PURE__ */ jsx("span", { children: "Base" }),
|
|
7040
|
+
/* @__PURE__ */ jsx(Price, { amount: priceResult.base_price })
|
|
7041
|
+
] }),
|
|
7042
|
+
priceResult.components_total !== "0" && /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary-line": true, children: [
|
|
7043
|
+
/* @__PURE__ */ jsx("span", { children: "Selections" }),
|
|
7044
|
+
/* @__PURE__ */ jsx(Price, { amount: priceResult.components_total })
|
|
7045
|
+
] }),
|
|
7046
|
+
/* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary-total": true, children: [
|
|
7047
|
+
/* @__PURE__ */ jsx("span", { children: "Total" }),
|
|
7048
|
+
/* @__PURE__ */ jsx(Price, { amount: priceResult.final_price })
|
|
7049
|
+
] })
|
|
7050
|
+
] }),
|
|
7051
|
+
isPriceLoading && /* @__PURE__ */ jsx("div", { "data-cimplify-composite-calculating": true, children: "Calculating price..." })
|
|
7052
|
+
] });
|
|
7053
|
+
}
|
|
7054
|
+
function ProductCustomizer({
|
|
7055
|
+
product,
|
|
7056
|
+
onAddToCart,
|
|
7057
|
+
className
|
|
7058
|
+
}) {
|
|
7059
|
+
const [quantity, setQuantity] = useState(1);
|
|
7060
|
+
const [isAdded, setIsAdded] = useState(false);
|
|
7061
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
7062
|
+
const [selectedVariantId, setSelectedVariantId] = useState();
|
|
7063
|
+
const [selectedVariant, setSelectedVariant] = useState();
|
|
7064
|
+
const [selectedAddOnOptionIds, setSelectedAddOnOptionIds] = useState([]);
|
|
7065
|
+
const [compositeSelections, setCompositeSelections] = useState([]);
|
|
7066
|
+
const [compositePrice, setCompositePrice] = useState(null);
|
|
7067
|
+
const [compositeReady, setCompositeReady] = useState(false);
|
|
7068
|
+
const [bundleSelections, setBundleSelections] = useState([]);
|
|
7069
|
+
const [bundleReady, setBundleReady] = useState(false);
|
|
7070
|
+
const cart = useCart();
|
|
7071
|
+
const productType = product.type || "product";
|
|
7072
|
+
const isComposite = productType === "composite";
|
|
7073
|
+
const isBundle = productType === "bundle";
|
|
7074
|
+
const isStandard = !isComposite && !isBundle;
|
|
7075
|
+
const hasVariants = isStandard && product.variants && product.variants.length > 0;
|
|
7076
|
+
const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;
|
|
7077
|
+
useEffect(() => {
|
|
7078
|
+
setQuantity(1);
|
|
7079
|
+
setIsAdded(false);
|
|
7080
|
+
setIsSubmitting(false);
|
|
7081
|
+
setSelectedVariantId(void 0);
|
|
7082
|
+
setSelectedVariant(void 0);
|
|
7083
|
+
setSelectedAddOnOptionIds([]);
|
|
7084
|
+
setCompositeSelections([]);
|
|
7085
|
+
setCompositePrice(null);
|
|
7086
|
+
setCompositeReady(false);
|
|
7087
|
+
setBundleSelections([]);
|
|
7088
|
+
setBundleReady(false);
|
|
7089
|
+
}, [product.id]);
|
|
7090
|
+
const selectedAddOnOptions = useMemo(() => {
|
|
7091
|
+
if (!product.add_ons) return [];
|
|
7092
|
+
const options = [];
|
|
7093
|
+
for (const addOn of product.add_ons) {
|
|
7094
|
+
for (const option of addOn.options) {
|
|
7095
|
+
if (selectedAddOnOptionIds.includes(option.id)) {
|
|
7096
|
+
options.push(option);
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
return options;
|
|
7101
|
+
}, [product.add_ons, selectedAddOnOptionIds]);
|
|
7102
|
+
const normalizedAddOnOptionIds = useMemo(() => {
|
|
7103
|
+
if (selectedAddOnOptionIds.length === 0) return [];
|
|
7104
|
+
return Array.from(new Set(selectedAddOnOptionIds.map((id) => id.trim()).filter(Boolean))).sort();
|
|
7105
|
+
}, [selectedAddOnOptionIds]);
|
|
7106
|
+
const localTotalPrice = useMemo(() => {
|
|
7107
|
+
if (isComposite && compositePrice) {
|
|
7108
|
+
return parsePrice(compositePrice.final_price) * quantity;
|
|
7109
|
+
}
|
|
7110
|
+
let price = parsePrice(product.default_price);
|
|
7111
|
+
if (selectedVariant?.price_adjustment) {
|
|
7112
|
+
price += parsePrice(selectedVariant.price_adjustment);
|
|
7113
|
+
}
|
|
7114
|
+
for (const option of selectedAddOnOptions) {
|
|
7115
|
+
if (option.default_price) {
|
|
7116
|
+
price += parsePrice(option.default_price);
|
|
7117
|
+
}
|
|
7118
|
+
}
|
|
7119
|
+
return price * quantity;
|
|
7120
|
+
}, [product.default_price, selectedVariant, selectedAddOnOptions, quantity, isComposite, compositePrice]);
|
|
7121
|
+
const requiredAddOnsSatisfied = useMemo(() => {
|
|
7122
|
+
if (!product.add_ons) return true;
|
|
7123
|
+
for (const addOn of product.add_ons) {
|
|
7124
|
+
if (addOn.is_required) {
|
|
7125
|
+
const selectedInGroup = selectedAddOnOptionIds.filter(
|
|
7126
|
+
(id) => addOn.options.some((opt) => opt.id === id)
|
|
7127
|
+
).length;
|
|
7128
|
+
const minRequired = addOn.min_selections || 1;
|
|
7129
|
+
if (selectedInGroup < minRequired) {
|
|
7130
|
+
return false;
|
|
7131
|
+
}
|
|
7132
|
+
}
|
|
7133
|
+
}
|
|
7134
|
+
return true;
|
|
7135
|
+
}, [product.add_ons, selectedAddOnOptionIds]);
|
|
7136
|
+
const quoteInput = useMemo(
|
|
7137
|
+
() => ({
|
|
7138
|
+
productId: product.id,
|
|
7139
|
+
quantity,
|
|
7140
|
+
variantId: selectedVariantId,
|
|
7141
|
+
addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : void 0
|
|
7142
|
+
}),
|
|
7143
|
+
[product.id, quantity, selectedVariantId, normalizedAddOnOptionIds]
|
|
7144
|
+
);
|
|
7145
|
+
const { quote } = useQuote(quoteInput, {
|
|
7146
|
+
enabled: isStandard && requiredAddOnsSatisfied
|
|
7147
|
+
});
|
|
7148
|
+
const quoteId = quote?.quote_id;
|
|
7149
|
+
const quotedTotalPrice = useMemo(() => {
|
|
7150
|
+
if (!quote) return void 0;
|
|
7151
|
+
const quotedTotal = quote.quoted_total_price_info?.final_price ?? quote.final_price_info.final_price;
|
|
7152
|
+
return quotedTotal === void 0 || quotedTotal === null ? void 0 : parsePrice(quotedTotal);
|
|
7153
|
+
}, [quote]);
|
|
7154
|
+
const displayTotalPrice = quotedTotalPrice ?? localTotalPrice;
|
|
7155
|
+
const canAddToCart = useMemo(() => {
|
|
7156
|
+
if (isComposite) return compositeReady;
|
|
7157
|
+
if (isBundle) return bundleReady;
|
|
7158
|
+
return requiredAddOnsSatisfied;
|
|
7159
|
+
}, [isComposite, isBundle, compositeReady, bundleReady, requiredAddOnsSatisfied]);
|
|
7160
|
+
const handleVariantChange = useCallback(
|
|
7161
|
+
(variantId, variant) => {
|
|
7162
|
+
setSelectedVariantId(variantId);
|
|
7163
|
+
setSelectedVariant(variant);
|
|
7164
|
+
},
|
|
7165
|
+
[]
|
|
7166
|
+
);
|
|
7167
|
+
const handleAddToCart = async () => {
|
|
7168
|
+
if (isSubmitting) return;
|
|
7169
|
+
setIsSubmitting(true);
|
|
7170
|
+
const options = {
|
|
7171
|
+
variantId: selectedVariantId,
|
|
7172
|
+
variant: selectedVariant ? { id: selectedVariant.id, name: selectedVariant.name || "", price_adjustment: selectedVariant.price_adjustment } : void 0,
|
|
7173
|
+
quoteId,
|
|
7174
|
+
addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : void 0,
|
|
7175
|
+
addOnOptions: selectedAddOnOptions.length > 0 ? selectedAddOnOptions.map((opt) => ({
|
|
7176
|
+
id: opt.id,
|
|
7177
|
+
name: opt.name,
|
|
7178
|
+
add_on_id: opt.add_on_id,
|
|
7179
|
+
default_price: opt.default_price
|
|
7180
|
+
})) : void 0,
|
|
7181
|
+
compositeSelections: isComposite && compositeSelections.length > 0 ? compositeSelections : void 0,
|
|
7182
|
+
bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : void 0
|
|
7183
|
+
};
|
|
7184
|
+
try {
|
|
7185
|
+
if (onAddToCart) {
|
|
7186
|
+
await onAddToCart(product, quantity, options);
|
|
7187
|
+
} else {
|
|
7188
|
+
await cart.addItem(product, quantity, options);
|
|
7189
|
+
}
|
|
7190
|
+
setIsAdded(true);
|
|
7191
|
+
setTimeout(() => {
|
|
7192
|
+
setIsAdded(false);
|
|
7193
|
+
setQuantity(1);
|
|
7194
|
+
}, 2e3);
|
|
7195
|
+
} catch {
|
|
7196
|
+
} finally {
|
|
7197
|
+
setIsSubmitting(false);
|
|
7198
|
+
}
|
|
7199
|
+
};
|
|
7200
|
+
return /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer": true, className, children: [
|
|
7201
|
+
isComposite && product.groups && product.composite_id && /* @__PURE__ */ jsx(
|
|
7202
|
+
CompositeSelector,
|
|
7203
|
+
{
|
|
7204
|
+
compositeId: product.composite_id,
|
|
7205
|
+
groups: product.groups,
|
|
7206
|
+
onSelectionsChange: setCompositeSelections,
|
|
7207
|
+
onPriceChange: setCompositePrice,
|
|
7208
|
+
onReady: setCompositeReady
|
|
7209
|
+
}
|
|
7210
|
+
),
|
|
7211
|
+
isBundle && product.components && /* @__PURE__ */ jsx(
|
|
7212
|
+
BundleSelector,
|
|
7213
|
+
{
|
|
7214
|
+
components: product.components,
|
|
7215
|
+
bundlePrice: product.bundle_price,
|
|
7216
|
+
discountValue: product.discount_value,
|
|
7217
|
+
onSelectionsChange: setBundleSelections,
|
|
7218
|
+
onReady: setBundleReady
|
|
7219
|
+
}
|
|
7220
|
+
),
|
|
7221
|
+
hasVariants && /* @__PURE__ */ jsx(
|
|
7222
|
+
VariantSelector,
|
|
7223
|
+
{
|
|
7224
|
+
variants: product.variants,
|
|
7225
|
+
variantAxes: product.variant_axes,
|
|
7226
|
+
basePrice: product.default_price,
|
|
7227
|
+
selectedVariantId,
|
|
7228
|
+
onVariantChange: handleVariantChange
|
|
7229
|
+
}
|
|
7230
|
+
),
|
|
7231
|
+
hasAddOns && /* @__PURE__ */ jsx(
|
|
7232
|
+
AddOnSelector,
|
|
7233
|
+
{
|
|
7234
|
+
addOns: product.add_ons,
|
|
7235
|
+
selectedOptions: selectedAddOnOptionIds,
|
|
7236
|
+
onOptionsChange: setSelectedAddOnOptionIds
|
|
7237
|
+
}
|
|
7238
|
+
),
|
|
7239
|
+
/* @__PURE__ */ jsxs("div", { "data-cimplify-customizer-actions": true, children: [
|
|
7240
|
+
/* @__PURE__ */ jsx(
|
|
7241
|
+
QuantitySelector,
|
|
7242
|
+
{
|
|
7243
|
+
value: quantity,
|
|
7244
|
+
onChange: setQuantity,
|
|
7245
|
+
min: 1
|
|
7246
|
+
}
|
|
7247
|
+
),
|
|
7248
|
+
/* @__PURE__ */ jsx(
|
|
7249
|
+
"button",
|
|
7250
|
+
{
|
|
7251
|
+
type: "button",
|
|
7252
|
+
onClick: handleAddToCart,
|
|
7253
|
+
disabled: isAdded || isSubmitting || !canAddToCart,
|
|
7254
|
+
"data-cimplify-customizer-submit": true,
|
|
7255
|
+
children: isAdded ? "Added to Cart" : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
7256
|
+
"Add to Cart \xB7 ",
|
|
7257
|
+
/* @__PURE__ */ jsx(Price, { amount: displayTotalPrice })
|
|
7258
|
+
] })
|
|
7259
|
+
}
|
|
7260
|
+
)
|
|
7261
|
+
] }),
|
|
7262
|
+
!canAddToCart && /* @__PURE__ */ jsx("p", { "data-cimplify-customizer-validation": true, children: "Please select all required options" })
|
|
7263
|
+
] });
|
|
7264
|
+
}
|
|
6749
7265
|
var ASPECT_STYLES = {
|
|
6750
7266
|
square: { aspectRatio: "1/1" },
|
|
6751
7267
|
"4/3": { aspectRatio: "4/3" },
|
|
@@ -6922,4 +7438,4 @@ function CartSummary({
|
|
|
6922
7438
|
] }) });
|
|
6923
7439
|
}
|
|
6924
7440
|
|
|
6925
|
-
export { Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, CartSummary, CimplifyCheckout, CimplifyProvider, ElementsProvider, PaymentElement, Price, ProductImageGallery, QuantitySelector, VariantSelector, getVariantDisplayName, useAds, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
|
|
7441
|
+
export { Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, BundleSelector, CartSummary, CimplifyCheckout, CimplifyProvider, CompositeSelector, ElementsProvider, PaymentElement, Price, ProductCustomizer, ProductImageGallery, QuantitySelector, VariantSelector, getVariantDisplayName, useAds, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
|