@cimplify/sdk 0.8.14 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.mjs CHANGED
@@ -597,18 +597,6 @@ function normalizeCatalogueProductPayload(product) {
597
597
  const derivedDefaultPrice = readFinalPrice(normalized["default_price_info"]) || readFinalPrice(normalized["price_info"]) || (typeof normalized["final_price"] === "string" || typeof normalized["final_price"] === "number" ? normalized["final_price"] : void 0);
598
598
  normalized["default_price"] = derivedDefaultPrice ?? "0";
599
599
  }
600
- const variants = normalized["variants"];
601
- if (Array.isArray(variants)) {
602
- normalized["variants"] = variants.map((variant) => {
603
- if (!isRecord(variant)) return variant;
604
- const normalizedVariant = { ...variant };
605
- const variantAdjustment = normalizedVariant["price_adjustment"];
606
- if (variantAdjustment === void 0 || variantAdjustment === null || variantAdjustment === "") {
607
- normalizedVariant["price_adjustment"] = readFinalPrice(normalizedVariant["price_info"]) ?? "0";
608
- }
609
- return normalizedVariant;
610
- });
611
- }
612
600
  const addOns = normalized["add_ons"];
613
601
  if (Array.isArray(addOns)) {
614
602
  normalized["add_ons"] = addOns.map((addOn) => normalizeAddOnPayload(addOn));
@@ -751,9 +739,7 @@ var CatalogueQueries = class {
751
739
  return safe(
752
740
  this.client.post(
753
741
  `/api/v1/catalogue/products/${encodedId}/variants/find`,
754
- {
755
- axis_selections: selections
756
- }
742
+ { axis_selections: selections }
757
743
  )
758
744
  );
759
745
  }
@@ -6772,5 +6758,920 @@ function useCheckout() {
6772
6758
  );
6773
6759
  return { submit, process, isLoading };
6774
6760
  }
6761
+ function QuantitySelector({
6762
+ value,
6763
+ onChange,
6764
+ min = 1,
6765
+ max,
6766
+ className
6767
+ }) {
6768
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-quantity": true, className, style: { display: "inline-flex", alignItems: "center", gap: "0.5rem" }, children: [
6769
+ /* @__PURE__ */ jsx(
6770
+ "button",
6771
+ {
6772
+ type: "button",
6773
+ onClick: () => onChange(Math.max(min, value - 1)),
6774
+ disabled: value <= min,
6775
+ "aria-label": "Decrease quantity",
6776
+ "data-cimplify-quantity-decrement": true,
6777
+ children: "\u2212"
6778
+ }
6779
+ ),
6780
+ /* @__PURE__ */ jsx("span", { "data-cimplify-quantity-value": true, "aria-live": "polite", children: value }),
6781
+ /* @__PURE__ */ jsx(
6782
+ "button",
6783
+ {
6784
+ type: "button",
6785
+ onClick: () => onChange(max != null ? Math.min(max, value + 1) : value + 1),
6786
+ disabled: max != null && value >= max,
6787
+ "aria-label": "Increase quantity",
6788
+ "data-cimplify-quantity-increment": true,
6789
+ children: "+"
6790
+ }
6791
+ )
6792
+ ] });
6793
+ }
6794
+
6795
+ // src/utils/variant.ts
6796
+ function getVariantDisplayName(variant) {
6797
+ if (variant.name) return variant.name;
6798
+ if (variant.display_attributes?.length) {
6799
+ return variant.display_attributes.map((a) => a.value_name).join(" / ");
6800
+ }
6801
+ return variant.is_default ? "Default" : "Option";
6802
+ }
6803
+ function VariantSelector({
6804
+ variants,
6805
+ variantAxes,
6806
+ basePrice,
6807
+ selectedVariantId,
6808
+ onVariantChange,
6809
+ className
6810
+ }) {
6811
+ const [axisSelections, setAxisSelections] = useState({});
6812
+ const initialized = useRef(false);
6813
+ useEffect(() => {
6814
+ initialized.current = false;
6815
+ }, [variants]);
6816
+ useEffect(() => {
6817
+ if (initialized.current) return;
6818
+ if (!variants || variants.length === 0) return;
6819
+ const defaultVariant = variants.find((v) => v.is_default) || variants[0];
6820
+ if (!defaultVariant) return;
6821
+ initialized.current = true;
6822
+ onVariantChange(defaultVariant.id, defaultVariant);
6823
+ if (defaultVariant.display_attributes) {
6824
+ const initial = {};
6825
+ for (const attr of defaultVariant.display_attributes) {
6826
+ initial[attr.axis_id] = attr.value_id;
6827
+ }
6828
+ setAxisSelections(initial);
6829
+ }
6830
+ }, [variants, onVariantChange]);
6831
+ useEffect(() => {
6832
+ if (!initialized.current) return;
6833
+ if (!variantAxes || variantAxes.length === 0) return;
6834
+ const match = variants.find((v) => {
6835
+ if (!v.display_attributes) return false;
6836
+ return v.display_attributes.every(
6837
+ (attr) => axisSelections[attr.axis_id] === attr.value_id
6838
+ );
6839
+ });
6840
+ if (match && match.id !== selectedVariantId) {
6841
+ onVariantChange(match.id, match);
6842
+ }
6843
+ }, [axisSelections, variants, variantAxes, selectedVariantId, onVariantChange]);
6844
+ if (!variants || variants.length <= 1) {
6845
+ return null;
6846
+ }
6847
+ const basePriceNum = basePrice != null ? parsePrice(basePrice) : 0;
6848
+ if (variantAxes && variantAxes.length > 0) {
6849
+ return /* @__PURE__ */ jsx("div", { "data-cimplify-variant-selector": true, className, children: variantAxes.map((axis) => /* @__PURE__ */ jsxs("div", { "data-cimplify-variant-axis": true, children: [
6850
+ /* @__PURE__ */ jsx("label", { "data-cimplify-variant-axis-label": true, children: axis.name }),
6851
+ /* @__PURE__ */ jsx("div", { "data-cimplify-variant-axis-options": true, children: axis.values.map((value) => {
6852
+ const isSelected = axisSelections[axis.id] === value.id;
6853
+ return /* @__PURE__ */ jsx(
6854
+ "button",
6855
+ {
6856
+ type: "button",
6857
+ "aria-selected": isSelected,
6858
+ onClick: () => {
6859
+ setAxisSelections((prev) => ({
6860
+ ...prev,
6861
+ [axis.id]: value.id
6862
+ }));
6863
+ },
6864
+ "data-cimplify-variant-option": true,
6865
+ "data-selected": isSelected || void 0,
6866
+ children: value.name
6867
+ },
6868
+ value.id
6869
+ );
6870
+ }) })
6871
+ ] }, axis.id)) });
6872
+ }
6873
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-variant-selector": true, className, children: [
6874
+ /* @__PURE__ */ jsx("label", { "data-cimplify-variant-list-label": true, children: "Options" }),
6875
+ /* @__PURE__ */ jsx("div", { "data-cimplify-variant-list": true, children: variants.map((variant) => {
6876
+ const isSelected = selectedVariantId === variant.id;
6877
+ const adjustment = parsePrice(variant.price_adjustment);
6878
+ const effectivePrice = basePriceNum + adjustment;
6879
+ return /* @__PURE__ */ jsxs(
6880
+ "button",
6881
+ {
6882
+ type: "button",
6883
+ "aria-selected": isSelected,
6884
+ onClick: () => onVariantChange(variant.id, variant),
6885
+ "data-cimplify-variant-option": true,
6886
+ "data-selected": isSelected || void 0,
6887
+ children: [
6888
+ /* @__PURE__ */ jsx("span", { "data-cimplify-variant-name": true, children: getVariantDisplayName(variant) }),
6889
+ /* @__PURE__ */ jsxs("span", { "data-cimplify-variant-pricing": true, children: [
6890
+ adjustment !== 0 && /* @__PURE__ */ jsxs("span", { "data-cimplify-variant-adjustment": true, children: [
6891
+ adjustment > 0 ? "+" : "",
6892
+ /* @__PURE__ */ jsx(Price, { amount: variant.price_adjustment })
6893
+ ] }),
6894
+ /* @__PURE__ */ jsx(Price, { amount: effectivePrice })
6895
+ ] })
6896
+ ]
6897
+ },
6898
+ variant.id
6899
+ );
6900
+ }) })
6901
+ ] });
6902
+ }
6903
+ function AddOnSelector({
6904
+ addOns,
6905
+ selectedOptions,
6906
+ onOptionsChange,
6907
+ className
6908
+ }) {
6909
+ const isOptionSelected = useCallback(
6910
+ (optionId) => selectedOptions.includes(optionId),
6911
+ [selectedOptions]
6912
+ );
6913
+ const toggleOption = useCallback(
6914
+ (addOn, optionId) => {
6915
+ const isSelected = selectedOptions.includes(optionId);
6916
+ if (addOn.is_mutually_exclusive || !addOn.is_multiple_allowed) {
6917
+ const groupOptionIds = new Set(addOn.options.map((o) => o.id));
6918
+ const withoutGroup = selectedOptions.filter((id) => !groupOptionIds.has(id));
6919
+ if (isSelected) {
6920
+ if (!addOn.is_required) {
6921
+ onOptionsChange(withoutGroup);
6922
+ }
6923
+ } else {
6924
+ onOptionsChange([...withoutGroup, optionId]);
6925
+ }
6926
+ } else {
6927
+ if (isSelected) {
6928
+ onOptionsChange(selectedOptions.filter((id) => id !== optionId));
6929
+ } else {
6930
+ const currentCount = selectedOptions.filter(
6931
+ (id) => addOn.options.some((o) => o.id === id)
6932
+ ).length;
6933
+ if (addOn.max_selections && currentCount >= addOn.max_selections) {
6934
+ return;
6935
+ }
6936
+ onOptionsChange([...selectedOptions, optionId]);
6937
+ }
6938
+ }
6939
+ },
6940
+ [selectedOptions, onOptionsChange]
6941
+ );
6942
+ if (!addOns || addOns.length === 0) {
6943
+ return null;
6944
+ }
6945
+ return /* @__PURE__ */ jsx("div", { "data-cimplify-addon-selector": true, className, children: addOns.map((addOn) => {
6946
+ const currentSelections = selectedOptions.filter(
6947
+ (id) => addOn.options.some((o) => o.id === id)
6948
+ ).length;
6949
+ const minMet = !addOn.min_selections || currentSelections >= addOn.min_selections;
6950
+ const isSingleSelect = addOn.is_mutually_exclusive || !addOn.is_multiple_allowed;
6951
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-addon-group": true, children: [
6952
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-addon-header": true, children: [
6953
+ /* @__PURE__ */ jsxs("div", { children: [
6954
+ /* @__PURE__ */ jsxs("span", { "data-cimplify-addon-name": true, children: [
6955
+ addOn.name,
6956
+ addOn.is_required && /* @__PURE__ */ jsx("span", { "data-cimplify-addon-required": true, children: " *" })
6957
+ ] }),
6958
+ (addOn.min_selections || addOn.max_selections) && /* @__PURE__ */ jsx("span", { "data-cimplify-addon-constraint": true, children: addOn.min_selections && addOn.max_selections ? `Choose ${addOn.min_selections}\u2013${addOn.max_selections}` : addOn.min_selections ? `Choose at least ${addOn.min_selections}` : `Choose up to ${addOn.max_selections}` })
6959
+ ] }),
6960
+ !minMet && /* @__PURE__ */ jsx("span", { "data-cimplify-addon-validation": true, children: "Required" })
6961
+ ] }),
6962
+ /* @__PURE__ */ jsx("div", { "data-cimplify-addon-options": true, children: addOn.options.map((option) => {
6963
+ const isSelected = isOptionSelected(option.id);
6964
+ return /* @__PURE__ */ jsxs(
6965
+ "button",
6966
+ {
6967
+ type: "button",
6968
+ role: isSingleSelect ? "radio" : "checkbox",
6969
+ "aria-checked": isSelected,
6970
+ onClick: () => toggleOption(addOn, option.id),
6971
+ "data-cimplify-addon-option": true,
6972
+ "data-selected": isSelected || void 0,
6973
+ children: [
6974
+ /* @__PURE__ */ jsx("span", { "data-cimplify-addon-option-name": true, children: option.name }),
6975
+ option.default_price && option.default_price !== "0" && /* @__PURE__ */ jsx(
6976
+ Price,
6977
+ {
6978
+ amount: option.default_price,
6979
+ prefix: "+"
6980
+ }
6981
+ )
6982
+ ]
6983
+ },
6984
+ option.id
6985
+ );
6986
+ }) })
6987
+ ] }, addOn.id);
6988
+ }) });
6989
+ }
6990
+ function CompositeSelector({
6991
+ productId,
6992
+ onSelectionsChange,
6993
+ onPriceChange,
6994
+ onReady,
6995
+ className
6996
+ }) {
6997
+ const { composite, isLoading, error, calculatePrice, priceResult, isPriceLoading } = useComposite(productId);
6998
+ const [groupSelections, setGroupSelections] = useState({});
6999
+ const selections = useMemo(() => {
7000
+ const result = [];
7001
+ for (const groupSels of Object.values(groupSelections)) {
7002
+ for (const [componentId, qty] of Object.entries(groupSels)) {
7003
+ if (qty > 0) {
7004
+ result.push({ component_id: componentId, quantity: qty });
7005
+ }
7006
+ }
7007
+ }
7008
+ return result;
7009
+ }, [groupSelections]);
7010
+ useEffect(() => {
7011
+ onSelectionsChange(selections);
7012
+ }, [selections, onSelectionsChange]);
7013
+ useEffect(() => {
7014
+ onPriceChange?.(priceResult);
7015
+ }, [priceResult, onPriceChange]);
7016
+ const allGroupsSatisfied = useMemo(() => {
7017
+ if (!composite) return false;
7018
+ for (const group of composite.groups) {
7019
+ const groupSels = groupSelections[group.id] || {};
7020
+ const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
7021
+ if (totalSelected < group.min_selections) return false;
7022
+ }
7023
+ return true;
7024
+ }, [composite, groupSelections]);
7025
+ useEffect(() => {
7026
+ onReady?.(allGroupsSatisfied);
7027
+ }, [allGroupsSatisfied, onReady]);
7028
+ useEffect(() => {
7029
+ if (allGroupsSatisfied && selections.length > 0) {
7030
+ void calculatePrice(selections);
7031
+ }
7032
+ }, [selections, allGroupsSatisfied, calculatePrice]);
7033
+ const toggleComponent = useCallback(
7034
+ (group, component) => {
7035
+ setGroupSelections((prev) => {
7036
+ const groupSels = { ...prev[group.id] || {} };
7037
+ const currentQty = groupSels[component.id] || 0;
7038
+ if (currentQty > 0) {
7039
+ if (group.min_selections > 0) {
7040
+ const totalOthers = Object.entries(groupSels).filter(([id]) => id !== component.id).reduce((sum, [, q]) => sum + q, 0);
7041
+ if (totalOthers < group.min_selections) {
7042
+ return prev;
7043
+ }
7044
+ }
7045
+ delete groupSels[component.id];
7046
+ } else {
7047
+ const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
7048
+ if (group.max_selections && totalSelected >= group.max_selections) {
7049
+ if (group.max_selections === 1) {
7050
+ return { ...prev, [group.id]: { [component.id]: 1 } };
7051
+ }
7052
+ return prev;
7053
+ }
7054
+ groupSels[component.id] = 1;
7055
+ }
7056
+ return { ...prev, [group.id]: groupSels };
7057
+ });
7058
+ },
7059
+ []
7060
+ );
7061
+ const updateQuantity = useCallback(
7062
+ (group, componentId, delta) => {
7063
+ setGroupSelections((prev) => {
7064
+ const groupSels = { ...prev[group.id] || {} };
7065
+ const current = groupSels[componentId] || 0;
7066
+ const next = Math.max(0, current + delta);
7067
+ if (group.max_quantity_per_component && next > group.max_quantity_per_component) {
7068
+ return prev;
7069
+ }
7070
+ if (next === 0) {
7071
+ delete groupSels[componentId];
7072
+ } else {
7073
+ groupSels[componentId] = next;
7074
+ }
7075
+ return { ...prev, [group.id]: groupSels };
7076
+ });
7077
+ },
7078
+ []
7079
+ );
7080
+ if (isLoading) {
7081
+ return /* @__PURE__ */ jsx("div", { "data-cimplify-composite-selector": true, "data-loading": true, className, children: "Loading options..." });
7082
+ }
7083
+ if (error || !composite) {
7084
+ return null;
7085
+ }
7086
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-selector": true, className, children: [
7087
+ composite.groups.sort((a, b) => a.display_order - b.display_order).map((group) => {
7088
+ const groupSels = groupSelections[group.id] || {};
7089
+ const totalSelected = Object.values(groupSels).reduce((sum, q) => sum + q, 0);
7090
+ const minMet = totalSelected >= group.min_selections;
7091
+ const isSingleSelect = group.max_selections === 1;
7092
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-group": true, children: [
7093
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-group-header": true, children: [
7094
+ /* @__PURE__ */ jsxs("div", { children: [
7095
+ /* @__PURE__ */ jsxs("span", { "data-cimplify-composite-group-name": true, children: [
7096
+ group.name,
7097
+ group.min_selections > 0 && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-required": true, children: " *" })
7098
+ ] }),
7099
+ group.description && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-group-description": true, children: group.description }),
7100
+ /* @__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" })
7101
+ ] }),
7102
+ !minMet && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-validation": true, children: "Required" })
7103
+ ] }),
7104
+ /* @__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) => {
7105
+ const qty = groupSels[component.id] || 0;
7106
+ const isSelected = qty > 0;
7107
+ const displayName = component.display_name || component.product_id || component.id;
7108
+ return /* @__PURE__ */ jsxs(
7109
+ "button",
7110
+ {
7111
+ type: "button",
7112
+ role: isSingleSelect ? "radio" : "checkbox",
7113
+ "aria-checked": isSelected,
7114
+ onClick: () => toggleComponent(group, component),
7115
+ "data-cimplify-composite-component": true,
7116
+ "data-selected": isSelected || void 0,
7117
+ children: [
7118
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-component-info": true, children: [
7119
+ /* @__PURE__ */ jsx("span", { "data-cimplify-composite-component-name": true, children: displayName }),
7120
+ component.is_popular && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-badge": "popular", children: "Popular" }),
7121
+ component.is_premium && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-badge": "premium", children: "Premium" }),
7122
+ component.display_description && /* @__PURE__ */ jsx("span", { "data-cimplify-composite-component-description": true, children: component.display_description }),
7123
+ component.calories != null && /* @__PURE__ */ jsxs("span", { "data-cimplify-composite-component-calories": true, children: [
7124
+ component.calories,
7125
+ " cal"
7126
+ ] })
7127
+ ] }),
7128
+ group.allow_quantity && isSelected && /* @__PURE__ */ jsxs(
7129
+ "span",
7130
+ {
7131
+ "data-cimplify-composite-qty": true,
7132
+ onClick: (e) => e.stopPropagation(),
7133
+ children: [
7134
+ /* @__PURE__ */ jsx(
7135
+ "button",
7136
+ {
7137
+ type: "button",
7138
+ onClick: () => updateQuantity(group, component.id, -1),
7139
+ "aria-label": `Decrease ${displayName} quantity`,
7140
+ children: "\u2212"
7141
+ }
7142
+ ),
7143
+ /* @__PURE__ */ jsx("span", { children: qty }),
7144
+ /* @__PURE__ */ jsx(
7145
+ "button",
7146
+ {
7147
+ type: "button",
7148
+ onClick: () => updateQuantity(group, component.id, 1),
7149
+ "aria-label": `Increase ${displayName} quantity`,
7150
+ children: "+"
7151
+ }
7152
+ )
7153
+ ]
7154
+ }
7155
+ ),
7156
+ component.price && component.price !== "0" && /* @__PURE__ */ jsx(Price, { amount: component.price, prefix: "+" })
7157
+ ]
7158
+ },
7159
+ component.id
7160
+ );
7161
+ }) })
7162
+ ] }, group.id);
7163
+ }),
7164
+ priceResult && /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary": true, children: [
7165
+ priceResult.base_price !== "0" && /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary-line": true, children: [
7166
+ /* @__PURE__ */ jsx("span", { children: "Base" }),
7167
+ /* @__PURE__ */ jsx(Price, { amount: priceResult.base_price })
7168
+ ] }),
7169
+ priceResult.components_total !== "0" && /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary-line": true, children: [
7170
+ /* @__PURE__ */ jsx("span", { children: "Selections" }),
7171
+ /* @__PURE__ */ jsx(Price, { amount: priceResult.components_total })
7172
+ ] }),
7173
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-composite-summary-total": true, children: [
7174
+ /* @__PURE__ */ jsx("span", { children: "Total" }),
7175
+ /* @__PURE__ */ jsx(Price, { amount: priceResult.final_price })
7176
+ ] })
7177
+ ] }),
7178
+ isPriceLoading && /* @__PURE__ */ jsx("div", { "data-cimplify-composite-calculating": true, children: "Calculating price..." })
7179
+ ] });
7180
+ }
7181
+ function BundleSelector({
7182
+ bundleIdOrSlug,
7183
+ onSelectionsChange,
7184
+ onReady,
7185
+ className
7186
+ }) {
7187
+ const { bundle, isLoading, error } = useBundle(bundleIdOrSlug);
7188
+ const [variantChoices, setVariantChoices] = useState({});
7189
+ useEffect(() => {
7190
+ if (!bundle) return;
7191
+ const defaults = {};
7192
+ for (const comp of bundle.components) {
7193
+ if (comp.component.variant_id) {
7194
+ defaults[comp.component.id] = comp.component.variant_id;
7195
+ } else if (comp.variants.length > 0) {
7196
+ const defaultVariant = comp.variants.find((v) => v.is_default) || comp.variants[0];
7197
+ if (defaultVariant) {
7198
+ defaults[comp.component.id] = defaultVariant.id;
7199
+ }
7200
+ }
7201
+ }
7202
+ setVariantChoices(defaults);
7203
+ }, [bundle]);
7204
+ const selections = useMemo(() => {
7205
+ if (!bundle) return [];
7206
+ return bundle.components.map((comp) => ({
7207
+ component_id: comp.component.id,
7208
+ variant_id: variantChoices[comp.component.id],
7209
+ quantity: comp.component.quantity
7210
+ }));
7211
+ }, [bundle, variantChoices]);
7212
+ useEffect(() => {
7213
+ onSelectionsChange(selections);
7214
+ }, [selections, onSelectionsChange]);
7215
+ useEffect(() => {
7216
+ onReady?.(bundle != null && selections.length > 0);
7217
+ }, [bundle, selections, onReady]);
7218
+ const handleVariantChange = useCallback(
7219
+ (componentId, variantId) => {
7220
+ setVariantChoices((prev) => ({ ...prev, [componentId]: variantId }));
7221
+ },
7222
+ []
7223
+ );
7224
+ if (isLoading) {
7225
+ return /* @__PURE__ */ jsx("div", { "data-cimplify-bundle-selector": true, "data-loading": true, className, children: "Loading bundle..." });
7226
+ }
7227
+ if (error || !bundle) {
7228
+ return null;
7229
+ }
7230
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-selector": true, className, children: [
7231
+ /* @__PURE__ */ jsx("span", { "data-cimplify-bundle-heading": true, children: "Included in this bundle" }),
7232
+ /* @__PURE__ */ jsx("div", { "data-cimplify-bundle-components": true, children: bundle.components.map((comp) => /* @__PURE__ */ jsx(
7233
+ BundleComponentCard,
7234
+ {
7235
+ data: comp,
7236
+ selectedVariantId: variantChoices[comp.component.id],
7237
+ onVariantChange: (variantId) => handleVariantChange(comp.component.id, variantId)
7238
+ },
7239
+ comp.component.id
7240
+ )) }),
7241
+ bundle.bundle_price && /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-summary": true, children: [
7242
+ /* @__PURE__ */ jsx("span", { children: "Bundle price" }),
7243
+ /* @__PURE__ */ jsx(Price, { amount: bundle.bundle_price })
7244
+ ] }),
7245
+ bundle.discount_value && /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-savings": true, children: [
7246
+ /* @__PURE__ */ jsx("span", { children: "You save" }),
7247
+ /* @__PURE__ */ jsx(Price, { amount: bundle.discount_value })
7248
+ ] })
7249
+ ] });
7250
+ }
7251
+ function BundleComponentCard({
7252
+ data,
7253
+ selectedVariantId,
7254
+ onVariantChange
7255
+ }) {
7256
+ const { component, product, variants } = data;
7257
+ const showVariantPicker = component.allow_variant_choice && variants.length > 1;
7258
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-component": true, children: [
7259
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-bundle-component-header": true, children: [
7260
+ /* @__PURE__ */ jsxs("div", { children: [
7261
+ component.quantity > 1 && /* @__PURE__ */ jsxs("span", { "data-cimplify-bundle-component-qty": true, children: [
7262
+ "\xD7",
7263
+ component.quantity
7264
+ ] }),
7265
+ /* @__PURE__ */ jsx("span", { "data-cimplify-bundle-component-name": true, children: product.name })
7266
+ ] }),
7267
+ /* @__PURE__ */ jsx(Price, { amount: product.default_price })
7268
+ ] }),
7269
+ showVariantPicker && /* @__PURE__ */ jsx("div", { "data-cimplify-bundle-variant-picker": true, children: variants.map((variant) => {
7270
+ const isSelected = selectedVariantId === variant.id;
7271
+ const adjustment = parsePrice(variant.price_adjustment);
7272
+ return /* @__PURE__ */ jsxs(
7273
+ "button",
7274
+ {
7275
+ type: "button",
7276
+ "aria-selected": isSelected,
7277
+ onClick: () => onVariantChange(variant.id),
7278
+ "data-cimplify-bundle-variant-option": true,
7279
+ "data-selected": isSelected || void 0,
7280
+ children: [
7281
+ getVariantDisplayName(variant),
7282
+ adjustment !== 0 && /* @__PURE__ */ jsxs("span", { "data-cimplify-bundle-variant-adjustment": true, children: [
7283
+ adjustment > 0 ? "+" : "",
7284
+ /* @__PURE__ */ jsx(Price, { amount: variant.price_adjustment })
7285
+ ] })
7286
+ ]
7287
+ },
7288
+ variant.id
7289
+ );
7290
+ }) })
7291
+ ] });
7292
+ }
7293
+ function ProductCustomizer({
7294
+ product,
7295
+ onAddToCart,
7296
+ className
7297
+ }) {
7298
+ const [quantity, setQuantity] = useState(1);
7299
+ const [isAdded, setIsAdded] = useState(false);
7300
+ const [isSubmitting, setIsSubmitting] = useState(false);
7301
+ const [selectedVariantId, setSelectedVariantId] = useState();
7302
+ const [selectedVariant, setSelectedVariant] = useState();
7303
+ const [selectedAddOnOptionIds, setSelectedAddOnOptionIds] = useState([]);
7304
+ const [compositeSelections, setCompositeSelections] = useState([]);
7305
+ const [compositePrice, setCompositePrice] = useState(null);
7306
+ const [compositeReady, setCompositeReady] = useState(false);
7307
+ const [bundleSelections, setBundleSelections] = useState([]);
7308
+ const [bundleReady, setBundleReady] = useState(false);
7309
+ const cart = useCart();
7310
+ const productType = product.product_type || "product";
7311
+ const isComposite = productType === "composite";
7312
+ const isBundle = productType === "bundle";
7313
+ const isStandard = !isComposite && !isBundle;
7314
+ const hasVariants = isStandard && product.variants && product.variants.length > 0;
7315
+ const hasAddOns = isStandard && product.add_ons && product.add_ons.length > 0;
7316
+ useEffect(() => {
7317
+ setQuantity(1);
7318
+ setIsAdded(false);
7319
+ setIsSubmitting(false);
7320
+ setSelectedVariantId(void 0);
7321
+ setSelectedVariant(void 0);
7322
+ setSelectedAddOnOptionIds([]);
7323
+ setCompositeSelections([]);
7324
+ setCompositePrice(null);
7325
+ setCompositeReady(false);
7326
+ setBundleSelections([]);
7327
+ setBundleReady(false);
7328
+ }, [product.id]);
7329
+ const selectedAddOnOptions = useMemo(() => {
7330
+ if (!product.add_ons) return [];
7331
+ const options = [];
7332
+ for (const addOn of product.add_ons) {
7333
+ for (const option of addOn.options) {
7334
+ if (selectedAddOnOptionIds.includes(option.id)) {
7335
+ options.push(option);
7336
+ }
7337
+ }
7338
+ }
7339
+ return options;
7340
+ }, [product.add_ons, selectedAddOnOptionIds]);
7341
+ const normalizedAddOnOptionIds = useMemo(() => {
7342
+ if (selectedAddOnOptionIds.length === 0) return [];
7343
+ return Array.from(new Set(selectedAddOnOptionIds.map((id) => id.trim()).filter(Boolean))).sort();
7344
+ }, [selectedAddOnOptionIds]);
7345
+ const localTotalPrice = useMemo(() => {
7346
+ if (isComposite && compositePrice) {
7347
+ return parsePrice(compositePrice.final_price) * quantity;
7348
+ }
7349
+ let price = parsePrice(product.default_price);
7350
+ if (selectedVariant?.price_adjustment) {
7351
+ price += parsePrice(selectedVariant.price_adjustment);
7352
+ }
7353
+ for (const option of selectedAddOnOptions) {
7354
+ if (option.default_price) {
7355
+ price += parsePrice(option.default_price);
7356
+ }
7357
+ }
7358
+ return price * quantity;
7359
+ }, [product.default_price, selectedVariant, selectedAddOnOptions, quantity, isComposite, compositePrice]);
7360
+ const requiredAddOnsSatisfied = useMemo(() => {
7361
+ if (!product.add_ons) return true;
7362
+ for (const addOn of product.add_ons) {
7363
+ if (addOn.is_required) {
7364
+ const selectedInGroup = selectedAddOnOptionIds.filter(
7365
+ (id) => addOn.options.some((opt) => opt.id === id)
7366
+ ).length;
7367
+ const minRequired = addOn.min_selections || 1;
7368
+ if (selectedInGroup < minRequired) {
7369
+ return false;
7370
+ }
7371
+ }
7372
+ }
7373
+ return true;
7374
+ }, [product.add_ons, selectedAddOnOptionIds]);
7375
+ const quoteInput = useMemo(
7376
+ () => ({
7377
+ productId: product.id,
7378
+ quantity,
7379
+ variantId: selectedVariantId,
7380
+ addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : void 0
7381
+ }),
7382
+ [product.id, quantity, selectedVariantId, normalizedAddOnOptionIds]
7383
+ );
7384
+ const { quote } = useQuote(quoteInput, {
7385
+ enabled: isStandard && requiredAddOnsSatisfied
7386
+ });
7387
+ const quoteId = quote?.quote_id;
7388
+ const quotedTotalPrice = useMemo(() => {
7389
+ if (!quote) return void 0;
7390
+ const quotedTotal = quote.quoted_total_price_info?.final_price ?? quote.final_price_info.final_price;
7391
+ return quotedTotal === void 0 || quotedTotal === null ? void 0 : parsePrice(quotedTotal);
7392
+ }, [quote]);
7393
+ const displayTotalPrice = quotedTotalPrice ?? localTotalPrice;
7394
+ const canAddToCart = useMemo(() => {
7395
+ if (isComposite) return compositeReady;
7396
+ if (isBundle) return bundleReady;
7397
+ return requiredAddOnsSatisfied;
7398
+ }, [isComposite, isBundle, compositeReady, bundleReady, requiredAddOnsSatisfied]);
7399
+ const handleVariantChange = useCallback(
7400
+ (variantId, variant) => {
7401
+ setSelectedVariantId(variantId);
7402
+ setSelectedVariant(variant);
7403
+ },
7404
+ []
7405
+ );
7406
+ const handleAddToCart = async () => {
7407
+ if (isSubmitting) return;
7408
+ setIsSubmitting(true);
7409
+ const options = {
7410
+ variantId: selectedVariantId,
7411
+ variant: selectedVariant ? { id: selectedVariant.id, name: selectedVariant.name || "", price_adjustment: selectedVariant.price_adjustment } : void 0,
7412
+ quoteId,
7413
+ addOnOptionIds: normalizedAddOnOptionIds.length > 0 ? normalizedAddOnOptionIds : void 0,
7414
+ addOnOptions: selectedAddOnOptions.length > 0 ? selectedAddOnOptions.map((opt) => ({
7415
+ id: opt.id,
7416
+ name: opt.name,
7417
+ add_on_id: opt.add_on_id,
7418
+ default_price: opt.default_price
7419
+ })) : void 0,
7420
+ compositeSelections: isComposite && compositeSelections.length > 0 ? compositeSelections : void 0,
7421
+ bundleSelections: isBundle && bundleSelections.length > 0 ? bundleSelections : void 0
7422
+ };
7423
+ try {
7424
+ if (onAddToCart) {
7425
+ await onAddToCart(product, quantity, options);
7426
+ } else {
7427
+ await cart.addItem(product, quantity, options);
7428
+ }
7429
+ setIsAdded(true);
7430
+ setTimeout(() => {
7431
+ setIsAdded(false);
7432
+ setQuantity(1);
7433
+ }, 2e3);
7434
+ } catch {
7435
+ } finally {
7436
+ setIsSubmitting(false);
7437
+ }
7438
+ };
7439
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer": true, className, children: [
7440
+ isComposite && /* @__PURE__ */ jsx(
7441
+ CompositeSelector,
7442
+ {
7443
+ productId: product.id,
7444
+ onSelectionsChange: setCompositeSelections,
7445
+ onPriceChange: setCompositePrice,
7446
+ onReady: setCompositeReady
7447
+ }
7448
+ ),
7449
+ isBundle && /* @__PURE__ */ jsx(
7450
+ BundleSelector,
7451
+ {
7452
+ bundleIdOrSlug: product.slug,
7453
+ onSelectionsChange: setBundleSelections,
7454
+ onReady: setBundleReady
7455
+ }
7456
+ ),
7457
+ hasVariants && /* @__PURE__ */ jsx(
7458
+ VariantSelector,
7459
+ {
7460
+ variants: product.variants,
7461
+ variantAxes: product.variant_axes,
7462
+ basePrice: product.default_price,
7463
+ selectedVariantId,
7464
+ onVariantChange: handleVariantChange
7465
+ }
7466
+ ),
7467
+ hasAddOns && /* @__PURE__ */ jsx(
7468
+ AddOnSelector,
7469
+ {
7470
+ addOns: product.add_ons,
7471
+ selectedOptions: selectedAddOnOptionIds,
7472
+ onOptionsChange: setSelectedAddOnOptionIds
7473
+ }
7474
+ ),
7475
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-customizer-actions": true, children: [
7476
+ /* @__PURE__ */ jsx(
7477
+ QuantitySelector,
7478
+ {
7479
+ value: quantity,
7480
+ onChange: setQuantity,
7481
+ min: 1
7482
+ }
7483
+ ),
7484
+ /* @__PURE__ */ jsx(
7485
+ "button",
7486
+ {
7487
+ type: "button",
7488
+ onClick: handleAddToCart,
7489
+ disabled: isAdded || isSubmitting || !canAddToCart,
7490
+ "data-cimplify-customizer-submit": true,
7491
+ children: isAdded ? "Added to Cart" : /* @__PURE__ */ jsxs(Fragment, { children: [
7492
+ "Add to Cart \xB7 ",
7493
+ /* @__PURE__ */ jsx(Price, { amount: displayTotalPrice })
7494
+ ] })
7495
+ }
7496
+ )
7497
+ ] }),
7498
+ !canAddToCart && /* @__PURE__ */ jsx("p", { "data-cimplify-customizer-validation": true, children: "Please select all required options" })
7499
+ ] });
7500
+ }
7501
+ var ASPECT_STYLES = {
7502
+ square: { aspectRatio: "1/1" },
7503
+ "4/3": { aspectRatio: "4/3" },
7504
+ "16/10": { aspectRatio: "16/10" },
7505
+ "3/4": { aspectRatio: "3/4" }
7506
+ };
7507
+ function ProductImageGallery({
7508
+ images,
7509
+ productName,
7510
+ aspectRatio = "4/3",
7511
+ className
7512
+ }) {
7513
+ const normalizedImages = useMemo(
7514
+ () => images.filter(
7515
+ (image) => typeof image === "string" && image.trim().length > 0
7516
+ ),
7517
+ [images]
7518
+ );
7519
+ const [selectedImage, setSelectedImage] = useState(0);
7520
+ useEffect(() => {
7521
+ setSelectedImage(0);
7522
+ }, [normalizedImages.length, productName]);
7523
+ if (normalizedImages.length === 0) {
7524
+ return null;
7525
+ }
7526
+ const activeImage = normalizedImages[selectedImage] || normalizedImages[0];
7527
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-image-gallery": true, className, children: [
7528
+ /* @__PURE__ */ jsx(
7529
+ "div",
7530
+ {
7531
+ "data-cimplify-image-gallery-main": true,
7532
+ style: { position: "relative", overflow: "hidden", ...ASPECT_STYLES[aspectRatio] },
7533
+ children: /* @__PURE__ */ jsx(
7534
+ "img",
7535
+ {
7536
+ src: activeImage,
7537
+ alt: productName,
7538
+ style: { width: "100%", height: "100%", objectFit: "cover" },
7539
+ "data-cimplify-image-gallery-active": true
7540
+ }
7541
+ )
7542
+ }
7543
+ ),
7544
+ normalizedImages.length > 1 && /* @__PURE__ */ jsx("div", { "data-cimplify-image-gallery-thumbnails": true, style: { display: "flex", gap: "0.5rem", marginTop: "0.75rem" }, children: normalizedImages.map((image, index) => /* @__PURE__ */ jsx(
7545
+ "button",
7546
+ {
7547
+ type: "button",
7548
+ onClick: () => setSelectedImage(index),
7549
+ "aria-selected": selectedImage === index,
7550
+ "data-cimplify-image-gallery-thumb": true,
7551
+ "data-selected": selectedImage === index || void 0,
7552
+ style: {
7553
+ width: "4rem",
7554
+ height: "4rem",
7555
+ overflow: "hidden",
7556
+ padding: 0,
7557
+ border: "none",
7558
+ cursor: "pointer"
7559
+ },
7560
+ children: /* @__PURE__ */ jsx(
7561
+ "img",
7562
+ {
7563
+ src: image,
7564
+ alt: "",
7565
+ style: { width: "100%", height: "100%", objectFit: "cover" }
7566
+ }
7567
+ )
7568
+ },
7569
+ `${image}-${index}`
7570
+ )) })
7571
+ ] });
7572
+ }
7573
+ function computeUnitPrice(item) {
7574
+ let price = parsePrice(item.product.default_price);
7575
+ if (item.variant?.price_adjustment) {
7576
+ price += parsePrice(item.variant.price_adjustment);
7577
+ }
7578
+ for (const option of item.addOnOptions || []) {
7579
+ if (option.default_price) {
7580
+ price += parsePrice(option.default_price);
7581
+ }
7582
+ }
7583
+ return Math.round(price * 100) / 100;
7584
+ }
7585
+ function CartSummary({
7586
+ onCheckout,
7587
+ onItemRemove,
7588
+ onQuantityChange,
7589
+ emptyMessage = "Your cart is empty",
7590
+ className
7591
+ }) {
7592
+ const { items, itemCount, subtotal, tax, total, isEmpty, removeItem, updateQuantity } = useCart();
7593
+ const handleRemove = (itemId) => {
7594
+ if (onItemRemove) {
7595
+ onItemRemove(itemId);
7596
+ } else {
7597
+ void removeItem(itemId);
7598
+ }
7599
+ };
7600
+ const handleQuantityChange = (itemId, qty) => {
7601
+ if (onQuantityChange) {
7602
+ onQuantityChange(itemId, qty);
7603
+ } else {
7604
+ void updateQuantity(itemId, qty);
7605
+ }
7606
+ };
7607
+ return /* @__PURE__ */ jsx("div", { "data-cimplify-cart-summary": true, className, children: isEmpty ? /* @__PURE__ */ jsx("div", { "data-cimplify-cart-empty": true, children: /* @__PURE__ */ jsx("p", { children: emptyMessage }) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7608
+ /* @__PURE__ */ jsx("div", { "data-cimplify-cart-items": true, children: items.map((item) => {
7609
+ const unitPrice = computeUnitPrice(item);
7610
+ const hasComposite = item.compositeSelections && item.compositeSelections.length > 0;
7611
+ const hasBundle = item.bundleSelections && item.bundleSelections.length > 0;
7612
+ return /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-item": true, children: [
7613
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-item-info": true, children: [
7614
+ /* @__PURE__ */ jsx("span", { "data-cimplify-cart-item-name": true, children: item.product.name }),
7615
+ item.variant && /* @__PURE__ */ jsx("span", { "data-cimplify-cart-item-variant": true, children: getVariantDisplayName(item.variant) }),
7616
+ item.addOnOptions && item.addOnOptions.length > 0 && /* @__PURE__ */ jsxs("span", { "data-cimplify-cart-item-addons": true, children: [
7617
+ "+ ",
7618
+ item.addOnOptions.map((opt) => opt.name).join(", ")
7619
+ ] }),
7620
+ (hasComposite || hasBundle) && /* @__PURE__ */ jsx("span", { "data-cimplify-cart-item-badge": true, children: hasComposite ? "Custom" : "Bundle" }),
7621
+ /* @__PURE__ */ jsx(Price, { amount: unitPrice })
7622
+ ] }),
7623
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-item-controls": true, children: [
7624
+ /* @__PURE__ */ jsx(
7625
+ QuantitySelector,
7626
+ {
7627
+ value: item.quantity,
7628
+ onChange: (qty) => handleQuantityChange(item.id, qty),
7629
+ min: 0
7630
+ }
7631
+ ),
7632
+ /* @__PURE__ */ jsx(
7633
+ "button",
7634
+ {
7635
+ type: "button",
7636
+ onClick: () => handleRemove(item.id),
7637
+ "data-cimplify-cart-item-remove": true,
7638
+ "aria-label": `Remove ${item.product.name}`,
7639
+ children: "Remove"
7640
+ }
7641
+ )
7642
+ ] })
7643
+ ] }, item.id);
7644
+ }) }),
7645
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-totals": true, children: [
7646
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-subtotal": true, children: [
7647
+ /* @__PURE__ */ jsxs("span", { children: [
7648
+ "Subtotal (",
7649
+ itemCount,
7650
+ " ",
7651
+ itemCount === 1 ? "item" : "items",
7652
+ ")"
7653
+ ] }),
7654
+ /* @__PURE__ */ jsx(Price, { amount: subtotal })
7655
+ ] }),
7656
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-tax": true, children: [
7657
+ /* @__PURE__ */ jsx("span", { children: "Tax" }),
7658
+ /* @__PURE__ */ jsx(Price, { amount: tax })
7659
+ ] }),
7660
+ /* @__PURE__ */ jsxs("div", { "data-cimplify-cart-total": true, children: [
7661
+ /* @__PURE__ */ jsx("span", { children: "Total" }),
7662
+ /* @__PURE__ */ jsx(Price, { amount: total })
7663
+ ] })
7664
+ ] }),
7665
+ onCheckout && /* @__PURE__ */ jsx(
7666
+ "button",
7667
+ {
7668
+ type: "button",
7669
+ onClick: onCheckout,
7670
+ "data-cimplify-cart-checkout": true,
7671
+ children: "Proceed to Checkout"
7672
+ }
7673
+ )
7674
+ ] }) });
7675
+ }
6775
7676
 
6776
- export { Ad, AdProvider, AddressElement, AuthElement, CimplifyCheckout, CimplifyProvider, ElementsProvider, PaymentElement, Price, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };
7677
+ export { Ad, AdProvider, AddOnSelector, AddressElement, AuthElement, BundleSelector, CartSummary, CimplifyCheckout, CimplifyProvider, CompositeSelector, ElementsProvider, PaymentElement, Price, ProductCustomizer, ProductImageGallery, QuantitySelector, VariantSelector, getVariantDisplayName, useAds, useBundle, useCart, useCategories, useCheckout, useCimplify, useCollection, useCollections, useComposite, useElements, useElementsReady, useLocations, useOptionalCimplify, useOrder, useProduct, useProducts, useQuote, useSearch };