@cimplify/sdk 0.8.14 → 0.9.0

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